Skip to content

drivers: syscon: bflb efuses #89108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/syscon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

zephyr_library()
zephyr_library_sources_ifdef(CONFIG_SYSCON_GENERIC syscon.c)
zephyr_library_sources_ifdef(CONFIG_SYSCON_BFLB_EFUSE syscon_bflb_efuse.c)
2 changes: 2 additions & 0 deletions drivers/syscon/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ config SYSCON_INIT_PRIORITY
initialized earlier in the startup cycle. If unsure, leave at default
value

source "drivers/syscon/Kconfig.bflb_efuse"

endif # SYSCON
9 changes: 9 additions & 0 deletions drivers/syscon/Kconfig.bflb_efuse
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2024-2025 MASSDRIVER EI (massdriver.space)
# SPDX-License-Identifier: Apache-2.0

config SYSCON_BFLB_EFUSE
bool "Bouffalolab EFUSEs driver"
default y
depends on DT_HAS_BFLB_EFUSE_ENABLED
help
E-Fuse access via SYSCON API for Bouffalolab platforms.
248 changes: 248 additions & 0 deletions drivers/syscon/syscon_bflb_efuse.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
/*
* Copyright (c) 2025 MASSDRIVER EI (massdriver.space)
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT bflb_efuse

#include <zephyr/drivers/syscon.h>
#include <zephyr/kernel.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(efuse_bflb, CONFIG_SYSCON_LOG_LEVEL);

#include <bouffalolab/bl60x/bflb_soc.h>
#include <bouffalolab/bl60x/hbn_reg.h>
#include <bouffalolab/bl60x/ef_ctrl_reg.h>
#include <bouffalolab/bl60x/extra_defines.h>

struct efuse_bflb_data {
uint8_t cache[DT_INST_PROP(0, size)];
bool cached;
};

struct efuse_bflb_config {
uintptr_t addr;
size_t size;
};

static inline void efuse_bflb_clock_settle(void)
{
__asm__ volatile (".rept 15 ; nop ; .endr");
}

/* 32 Mhz Oscillator: 0
* crystal: 1
* PLL and 32M: 2
* PLL and crystal: 3
*/
Comment on lines +35 to +39
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could have used enum for these

enum bflb_root_clock {
	BFLB_CLOCK_32M_OSC,
	BFLB_CLOCK_XTAL,
	BFLB_CLOCK_PLL_32M_OSC,
	BFLB_CLOCK_PLL_XTAL,
	BFLB_CLOCK_INVALID,
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clock driver with this functionality and many others (and enums/defines) is next, but it is dependant on this driver.

Copy link
Member

@ycsin ycsin May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clock driver with this functionality and many others (and enums/defines) is next

We shouldn't depend on upcoming PRs since they are not guaranteed, but this is reasonably commented so 🤷‍♂️

static void efuse_bflb_set_root_clock(uint32_t clock)
{
uint32_t tmp;

/* invalid value, fallback to internal 32M */
if (clock > 3) {
clock = 0;
}
Comment on lines +44 to +47
Copy link
Member

@ycsin ycsin May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then this would naturally be:

Suggested change
/* invalid value, fallback to internal 32M */
if (clock > 3) {
clock = 0;
}
if (clock >= BFLB_CLOCK_INVALID) {
clock = BFLB_CLOCK_32M_OSC;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See clock driver comment

tmp = sys_read32(HBN_BASE + HBN_GLB_OFFSET);
tmp = (tmp & HBN_ROOT_CLK_SEL_UMSK) | (clock << HBN_ROOT_CLK_SEL_POS);
sys_write32(tmp, HBN_BASE + HBN_GLB_OFFSET);

efuse_bflb_clock_settle();
}

static void efuse_bflb_clock_delay_32M_ms(uint32_t ms)
{
uint32_t count = 0;

do {
__asm__ volatile (".rept 32 ; nop ; .endr");
count++;
} while (count < ms);
}

static uint32_t efuse_bflb_is_pds_busy(const struct device *dev)
{
uint32_t tmp;
const struct efuse_bflb_config *config = dev->config;

tmp = sys_read32(config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
if (tmp & EF_CTRL_EF_IF_0_BUSY_MSK) {
return 1;
}
return 0;
}

/* /!\ only use when running on 32Mhz Oscillator Clock
* (system_set_root_clock(0);
* system_set_root_clock_dividers(0, 0);
* sys_write32(32 * 1000 * 1000, CORECLOCKREGISTER);)
* Only Use with IRQs off
* returns 0 when error
*/
static void efuse_bflb_efuse_read(const struct device *dev)
{
const struct efuse_bflb_config *config = dev->config;
uint32_t tmp;
uint32_t *pefuse_start = (uint32_t *)(config->addr);
uint32_t timeout = 0;

do {
efuse_bflb_clock_delay_32M_ms(1);
timeout++;
} while (timeout < EF_CTRL_DFT_TIMEOUT_VAL && efuse_bflb_is_pds_busy(dev) > 0);

/* do a 'ahb clock' setup */
tmp = EF_CTRL_EFUSE_CTRL_PROTECT
| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
#if defined(CONFIG_SOC_SERIES_BL60X)
| (EF_CTRL_SAHB_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
#endif
| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
| (0 << EF_CTRL_EF_IF_0_RW_POS)
| (0 << EF_CTRL_EF_IF_0_TRIG_POS);

sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
efuse_bflb_clock_settle();

/* clear PDS cache registry */
for (uint32_t i = 0; i < config->size / 4; i++) {
pefuse_start[i] = 0;
}

/* Load efuse region0 */
/* not ahb clock setup */
tmp = EF_CTRL_EFUSE_CTRL_PROTECT
| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
#if defined(CONFIG_SOC_SERIES_BL60X)
| (EF_CTRL_EF_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
#endif
| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
| (0 << EF_CTRL_EF_IF_0_RW_POS)
| (0 << EF_CTRL_EF_IF_0_TRIG_POS);
sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);

/* trigger read */
tmp = EF_CTRL_EFUSE_CTRL_PROTECT
| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
#if defined(CONFIG_SOC_SERIES_BL60X)
| (EF_CTRL_EF_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
#endif
| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
| (0 << EF_CTRL_EF_IF_0_RW_POS)
| (1 << EF_CTRL_EF_IF_0_TRIG_POS);
sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
efuse_bflb_clock_delay_32M_ms(5);

/* wait for read to complete */
do {
efuse_bflb_clock_delay_32M_ms(1);
tmp = sys_read32(config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
} while ((tmp & EF_CTRL_EF_IF_0_BUSY_MSK) ||
!(tmp & EF_CTRL_EF_IF_0_AUTOLOAD_DONE_MSK));

/* do a 'ahb clock' setup */
tmp = EF_CTRL_EFUSE_CTRL_PROTECT
| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
#if defined(CONFIG_SOC_SERIES_BL60X)
| (EF_CTRL_SAHB_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
#endif
| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
| (0 << EF_CTRL_EF_IF_0_RW_POS)
| (0 << EF_CTRL_EF_IF_0_TRIG_POS);

sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
}

static void efuse_bflb_cache(const struct device *dev)
{
struct efuse_bflb_data *data = dev->data;
const struct efuse_bflb_config *config = dev->config;
uint32_t tmp;
uint8_t old_clock_root;

tmp = sys_read32(HBN_BASE + HBN_GLB_OFFSET);
old_clock_root = (tmp & HBN_ROOT_CLK_SEL_MSK) >> HBN_ROOT_CLK_SEL_POS;
Comment on lines +177 to +178
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we can have a matching helper efuse_bflb_get_root_clock() function

Suggested change
tmp = sys_read32(HBN_BASE + HBN_GLB_OFFSET);
old_clock_root = (tmp & HBN_ROOT_CLK_SEL_MSK) >> HBN_ROOT_CLK_SEL_POS;
old_clock_root = efuse_bflb_get_root_clock();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See clock driver comment


efuse_bflb_set_root_clock(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the enum suggestion above is adopted:

Suggested change
efuse_bflb_set_root_clock(0);
efuse_bflb_set_root_clock(BFLB_CLOCK_32M_OSC);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See clock driver comment

efuse_bflb_clock_settle();

efuse_bflb_efuse_read(dev);
/* reads *must* be 32-bits aligned AND does not work with the method memcpy uses */
for (int i = 0; i < config->size / sizeof(uint32_t); i++) {
tmp = sys_read32(config->addr + i * 4);
data->cache[i * sizeof(uint32_t) + 3] = (tmp & 0xFF000000U) >> 24;
data->cache[i * sizeof(uint32_t) + 2] = (tmp & 0x00FF0000U) >> 16;
data->cache[i * sizeof(uint32_t) + 1] = (tmp & 0x0000FF00U) >> 8;
data->cache[i * sizeof(uint32_t) + 0] = (tmp & 0x000000FFU);
}

efuse_bflb_set_root_clock(old_clock_root);
efuse_bflb_clock_settle();
data->cached = true;
}

static int efuse_bflb_read(const struct device *dev, uint16_t reg, uint32_t *val)
{
struct efuse_bflb_data *data = dev->data;

if (!val) {
return -EINVAL;
}

if (!data->cached) {
efuse_bflb_cache(dev);
}

*val = *((uint32_t *)&data->cache[reg]);
return 0;
}

static int efuse_bflb_size(const struct device *dev, size_t *size)
{
const struct efuse_bflb_config *config = dev->config;

*size = config->size;
return 0;
}

static int efuse_bflb_get_base(const struct device *dev, uintptr_t *addr)
{
struct efuse_bflb_data *data = dev->data;

*addr = (uintptr_t)data->cache;
return 0;
}

static DEVICE_API(syscon, efuse_bflb_api) = {
.read = efuse_bflb_read,
.get_size = efuse_bflb_size,
.get_base = efuse_bflb_get_base,
};

static const struct efuse_bflb_config efuse_config = {
.addr = DT_INST_REG_ADDR(0),
.size = DT_INST_PROP(0, size),
};

static struct efuse_bflb_data efuse_data = {
.cached = false,
.cache = {0},
};


DEVICE_DT_INST_DEFINE(0, NULL, NULL, &efuse_data, &efuse_config, POST_KERNEL,
CONFIG_SYSCON_INIT_PRIORITY, &efuse_bflb_api);
17 changes: 17 additions & 0 deletions dts/bindings/syscon/bflb,efuse.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright (c) 2024-2025 MASSDRIVER EI (massdriver.space)
# SPDX-License-Identifier: Apache-2.0

description: BouffaloLab Efuse

compatible: "bflb,efuse"

include: base.yaml

properties:
reg:
required: true

size:
required: true
type: int
description: Size of efuse storage (bytes)
7 changes: 7 additions & 0 deletions dts/riscv/bflb/bl60x.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@
};
};

efuse: efuse@40007000 {
compatible = "bflb,efuse";
reg = <0x40007000 0x1000>;
status = "okay";
size = <128>;
};

uart0: uart@4000a000 {
compatible = "bflb,uart";
reg = <0x4000a000 0x100>;
Expand Down
7 changes: 4 additions & 3 deletions soc/bflb/bl60x/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
# SPDX-License-Identifier: Apache-2.0

config SOC_SERIES_BL60X
select ATOMIC_OPERATIONS_C
select CPU_HAS_FPU
select INCLUDE_RESET_VECTOR
select RISCV
select RISCV_MACHINE_TIMER
select RISCV_ISA_RV32I
Expand All @@ -11,8 +14,6 @@ config SOC_SERIES_BL60X
select RISCV_ISA_EXT_C
select RISCV_ISA_EXT_ZICSR
select RISCV_ISA_EXT_ZIFENCEI
select ATOMIC_OPERATIONS_C
select CPU_HAS_FPU
select INCLUDE_RESET_VECTOR
select SOC_EARLY_INIT_HOOK
select SYSCON
select XIP
2 changes: 1 addition & 1 deletion west.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ manifest:
- hal
- name: hal_bouffalolab
path: modules/hal/bouffalolab
revision: c6c44b879503d990dfba643c212b606cee414faa
revision: 5811738e2be348f30dc97d78280f2735d5d14084
groups:
- hal
- name: hal_espressif
Expand Down