Skip to content
Open
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/clock_management/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_MANAGEMENT_MUX clock_mux.c)
add_clock_management_header(clock_management_drivers.h)

add_subdirectory(common)
add_subdirectory(adi_max32)
add_subdirectory(nxp_syscon)

# Include headers for all clock management drivers, registered with add_clock_management_header
Expand Down
1 change: 1 addition & 0 deletions drivers/clock_management/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ config CLOCK_MANAGEMENT_MUX
A generic mux driver where a simple bitfield will unconditionally
select a clock parent.

source "drivers/clock_management/adi_max32/Kconfig"
source "drivers/clock_management/nxp_syscon/Kconfig"

module = CLOCK_MANAGEMENT
Expand Down
8 changes: 8 additions & 0 deletions drivers/clock_management/adi_max32/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright 2026 Analog Devices, Inc.
# SPDX-License-Identifier: Apache-2.0

zephyr_library_sources_ifdef(CONFIG_CLOCK_MANAGEMENT_ADI_MAX32_SOURCE max32_source.c)
zephyr_library_sources_ifdef(CONFIG_CLOCK_MANAGEMENT_ADI_MAX32_DIV max32_div.c)

# Header
add_clock_management_header_ifdef(CONFIG_CLOCK_MANAGEMENT_ADI_MAX32 max32_clocks.h)
23 changes: 23 additions & 0 deletions drivers/clock_management/adi_max32/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2026 Analog Devices, Inc.
# SPDX-License-Identifier: Apache-2.0

config CLOCK_MANAGEMENT_ADI_MAX32
bool
help
ADI MAX32 clock drivers

config CLOCK_MANAGEMENT_ADI_MAX32_SOURCE
bool "ADI MAX32 clock source driver"
default y
depends on DT_HAS_ADI_MAX32_CLOCK_SOURCE_ENABLED
select CLOCK_MANAGEMENT_ADI_MAX32
help
ADI MAX32 clock source driver

config CLOCK_MANAGEMENT_ADI_MAX32_DIV
bool "ADI MAX32 clock source divider"
default y
depends on DT_HAS_ADI_MAX32_CLOCK_DIV_ENABLED
select CLOCK_MANAGEMENT_ADI_MAX32
help
ADI MAX32 clock source divider/prescaler
31 changes: 31 additions & 0 deletions drivers/clock_management/adi_max32/max32_clocks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2026 Analog Devices, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_DRIVERS_CLOCK_MANAGEMENT_ADI_MAX32_CLOCKS_H_
#define ZEPHYR_DRIVERS_CLOCK_MANAGEMENT_ADI_MAX32_CLOCKS_H_


#ifdef __cplusplus
extern "C" {
#endif

/** @cond INTERNAL_HIDDEN */

#define Z_CLOCK_MANAGEMENT_DATA_DEFINE_adi_max32_clock_source(node_id, prop, idx)
#define Z_CLOCK_MANAGEMENT_DATA_GET_adi_max32_clock_source(node_id, prop, idx) \
DT_PHA_BY_IDX(node_id, prop, idx, gate)

#define Z_CLOCK_MANAGEMENT_DATA_DEFINE_adi_max32_clock_div(node_id, prop, idx)
#define Z_CLOCK_MANAGEMENT_DATA_GET_adi_max32_clock_div(node_id, prop, idx) \
DT_PHA_BY_IDX(node_id, prop, idx, divider)

/** @endcond */

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_DRIVERS_CLOCK_MANAGEMENT_ADI_MAX32_CLOCKS_H_ */
111 changes: 111 additions & 0 deletions drivers/clock_management/adi_max32/max32_div.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright 2026 Analog Devices, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/drivers/clock_management/clock_driver.h>

#include <wrap_max32_sys.h>

#define DT_DRV_COMPAT adi_max32_clock_div

/* Valid values are from 0 to 7 for the clock divider */
#define MAX32_CLOCK_DIVIDER_RANGE (7U)

struct max32_clock_div_config {
STANDARD_CLK_SUBSYS_DATA_DEFINE
volatile uint32_t *reg;
uint8_t mask_width;
uint8_t mask_offset;
};

static int max32_clock_div_configure(const struct clk *clk_hw, const void *data)
{
const struct max32_clock_div_config *config = clk_hw->hw_data;
const uint32_t div_setting = (uint32_t)(uintptr_t)data;

if (config->reg == NULL) {
return -EINVAL;
}

if (div_setting > MAX32_CLOCK_DIVIDER_RANGE) {
return -EINVAL;
}

Wrap_MXC_SYS_SetClockDiv(div_setting << MXC_F_GCR_CLKCTRL_SYSCLK_DIV_POS);

return 0;
}

static clock_freq_t max32_clock_div_recalc_rate(const struct clk *clk_hw, clock_freq_t parent_rate)
{
const struct max32_clock_div_config *config = clk_hw->hw_data;
const uint32_t div_setting = (*config->reg >> config->mask_offset) & GENMASK((config->mask_width - 1), 0);

if (config->reg == NULL) {
return -EINVAL;
}

return (parent_rate >> div_setting);
}

#if defined(CONFIG_CLOCK_MANAGEMENT_RUNTIME)
static clock_freq_t max32_clock_div_configure_recalc(const struct clk *clk_hw, const void *data, clock_freq_t parent_rate)
{
ARG_UNUSED(clk_hw);
const uint32_t div_setting = (uint32_t)(uintptr_t)data;

if (div_setting > MAX32_CLOCK_DIVIDER_RANGE) {
return -EINVAL;
}

return (parent_rate >> div_setting);
}
#endif

#if defined(CONFIG_CLOCK_MANAGEMENT_SET_RATE)
static clock_freq_t max32_clock_div_best_rate(const struct clk *clk_hw, clock_freq_t rate_req, clock_freq_t parent_rate, bool commit)
{
int ret;
const struct max32_clock_div_config *config = clk_hw->hw_data;

if (config->reg == NULL || rate_req == 0) {
return -EINVAL;
}

const uint32_t div_setting = MIN(MAX(LOG2(parent_rate / rate_req), 0), MAX32_CLOCK_DIVIDER_RANGE);
const clock_freq_t best_rate = (parent_rate >> div_setting);

if (commit) {
ret = max32_clock_div_configure(clk_hw, (const void *)(uintptr_t)div_setting);
if (ret < 0) {
return ret;
}
}

return best_rate;
}
#endif

const struct clock_management_standard_api max32_clock_div_api = {
.shared.configure = max32_clock_div_configure,
.recalc_rate = max32_clock_div_recalc_rate,
#if defined(CONFIG_CLOCK_MANAGEMENT_RUNTIME)
.configure_recalc = max32_clock_div_configure_recalc,
#endif
#if defined(CONFIG_CLOCK_MANAGEMENT_SET_RATE)
.best_rate = max32_clock_div_best_rate,
#endif
};

#define ADI_MAX32_CLOCK_DIV_DEFINE(inst) \
static const struct max32_clock_div_config max32_clock_div_config_##inst = { \
STANDARD_CLK_SUBSYS_DATA_INIT(CLOCK_DT_GET(DT_PHANDLE(DT_DRV_INST(inst), input_source))) \
.reg = (volatile uint32_t *)DT_INST_REG_ADDR(inst), \
.mask_width = (uint8_t)DT_INST_REG_SIZE(inst), \
.mask_offset = (uint8_t)DT_INST_PROP(inst, offset), \
}; \
CLOCK_DT_INST_DEFINE(inst, &max32_clock_div_config_##inst, &max32_clock_div_api);

DT_INST_FOREACH_STATUS_OKAY(ADI_MAX32_CLOCK_DIV_DEFINE)
96 changes: 96 additions & 0 deletions drivers/clock_management/adi_max32/max32_source.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright 2026 Analog Devices, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/drivers/clock_management/clock_driver.h>

#include <wrap_max32_sys.h>

#define DT_DRV_COMPAT adi_max32_clock_source

struct max32_clock_source_config {
uint8_t enable_offset;
uint32_t frequency;
volatile uint32_t *reg;
uint8_t enum_val;
};

static int max32_clock_source_configure(const struct clk *clk_hw, const void *data)
{
const struct max32_clock_source_config *config = clk_hw->hw_data;
bool ungate = (bool)data;

if (ungate) {
MXC_SYS_ClockSourceEnable(config->enum_val);
} else {
MXC_SYS_ClockSourceDisable(config->enum_val);
}

return 0;
}

static int max32_clock_source_get_rate(const struct clk *clk_hw)
{
const struct max32_clock_source_config *config = clk_hw->hw_data;

/* If the clock is enabled, return its frequency, otherwise return 0 */
return ((*config->reg) & BIT(config->enable_offset)) ? config->frequency : 0;
}

#if defined(CONFIG_CLOCK_MANAGEMENT_RUNTIME)
static clock_freq_t max32_clock_source_configure_recalc(const struct clk *clk_hw, const void *data)
{
const struct max32_clock_source_config *config = clk_hw->hw_data;
bool ungate = (bool)data;

/* If the clock is to be enabled, return its frequency, otherwise return 0 */
return ungate ? config->frequency : 0;
}
#endif

#if defined(CONFIG_CLOCK_MANAGEMENT_SET_RATE)
static clock_freq_t max32_clock_source_best_rate(const struct clk *clk_hw, clock_freq_t rate_req, bool commit)
{
const struct max32_clock_source_config *config = clk_hw->hw_data;

/* This clock source can only support its defined frequency, or 0 if gated */
if (commit) {
if (rate_req == 0) {
max32_clock_source_configure(clk_hw, (void *)false);
} else if (rate_req == config->frequency) {
max32_clock_source_configure(clk_hw, (void *)true);
} else {
return -EINVAL;
}
}

return (rate_req != 0) ? config->frequency : 0;
}
#endif

const struct clock_management_root_api max32_clock_source_api = {
.shared.configure = max32_clock_source_configure,
.get_rate = max32_clock_source_get_rate,
#if defined(CONFIG_CLOCK_MANAGEMENT_RUNTIME)
.root_configure_recalc = max32_clock_source_configure_recalc,
#endif
#if defined(CONFIG_CLOCK_MANAGEMENT_SET_RATE)
.root_best_rate = max32_clock_source_best_rate,
#endif
};

#define ADI_MAX32_CLOCK_SOURCE_DEFINE(inst) \
static const struct max32_clock_source_config max32_clock_source_config_##inst = { \
.frequency = DT_INST_PROP(inst, frequency), \
.reg = (volatile uint32_t *)DT_INST_REG_ADDR(inst), \
.enable_offset = (uint8_t)DT_INST_PROP(inst, offset), \
.enum_val = DT_INST_PROP(inst, enum), \
}; \
\
ROOT_CLOCK_DT_INST_DEFINE(inst, \
&max32_clock_source_config_##inst, \
&max32_clock_source_api)

DT_INST_FOREACH_STATUS_OKAY(ADI_MAX32_CLOCK_SOURCE_DEFINE)
21 changes: 19 additions & 2 deletions drivers/clock_management/clock_gate.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,30 @@ struct clock_gate_config {
STANDARD_CLK_SUBSYS_DATA_DEFINE
uintptr_t reg;
uint8_t enable_offset;
bool invert;
};

static clock_freq_t clock_gate_recalc_rate(const struct clk *clk_hw, clock_freq_t parent_rate)
{
const struct clock_gate_config *config = clk_hw->hw_data;
bool enabled = (sys_read32(config->reg) & BIT(config->enable_offset)) != 0;

return (sys_read32(config->reg) & BIT(config->enable_offset)) ? parent_rate : 0;
if (config->invert) {
enabled = !enabled;
}

return enabled ? parent_rate : 0;
}

static int clock_gate_configure(const struct clk *clk_hw, const void *data)
{
const struct clock_gate_config *config = clk_hw->hw_data;
bool ungate = (bool)data;

if (config->invert) {
ungate = !ungate;
}

if (ungate) {
sys_write32(sys_read32(config->reg) | BIT(config->enable_offset), config->reg);
} else {
Expand Down Expand Up @@ -55,15 +65,21 @@ static clock_freq_t clock_gate_best_rate(const struct clk *clk_hw, clock_freq_t
{
if (commit) {
const struct clock_gate_config *config = clk_hw->hw_data;
bool enable = (rate_req != 0);

if (rate_req != 0) {
if (config->invert) {
enable = !enable;
}

if (enable) {
sys_write32(sys_read32(config->reg) | BIT(config->enable_offset),
config->reg);
} else {
sys_write32(sys_read32(config->reg) & ~BIT(config->enable_offset),
config->reg);
}
}

return (rate_req != 0) ? parent_rate : 0;
}
#endif
Expand All @@ -84,6 +100,7 @@ const struct clock_management_standard_api clock_gate_api = {
STANDARD_CLK_SUBSYS_DATA_INIT(CLOCK_DT_GET(DT_INST_PHANDLE(inst, input))).reg = \
DT_INST_REG_ADDR(inst), \
.enable_offset = (uint8_t)DT_INST_PROP(inst, offset), \
.invert = (bool)DT_INST_PROP(inst, invert), \
}; \
\
CLOCK_DT_INST_DEFINE(inst, &clock_gate_##inst, &clock_gate_api);
Expand Down
Loading