Skip to content

Commit 788a48f

Browse files
committed
drivers: clock control: ironside dvfs hsfll
Extended clock control driver to support new DVFS service from IronSide secure domain. Added new compatible nrf-iron-hsfll-local which can be used to enable new DVFS service support in local domain. Signed-off-by: Łukasz Stępnicki <lukasz.stepnicki@nordicsemi.no>
1 parent d41716f commit 788a48f

File tree

5 files changed

+329
-0
lines changed

5 files changed

+329
-0
lines changed

boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_iron.dts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
status = "okay";
2323
};
2424

25+
&cpuapp_hsfll{
26+
compatible = "nordic,nrf-iron-hsfll-local";
27+
};
28+
2529
ironside_se_boot_report: &cpuapp_ironside_se_boot_report {};
2630

2731
boot_partition: &cpuapp_boot_partition {

drivers/clock_control/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF2_COMMON clock_cont
5353
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_FLL16M clock_control_nrf_fll16m.c)
5454
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF54H_HFXO clock_control_nrf54h_hfxo.c)
5555
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_HSFLL_LOCAL clock_control_nrf_hsfll_local.c)
56+
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL clock_control_nrf_iron_hsfll_local.c)
5657
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_LFCLK clock_control_nrf_lfclk.c)
5758
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BOUFFALOLAB_BL60X clock_control_bl60x.c)
5859

drivers/clock_control/Kconfig.nrf

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,26 @@ config CLOCK_CONTROL_NRF_HSFLL_LOCAL_NRFS_DVFS_TIMEOUT_MS
287287

288288
endif # CLOCK_CONTROL_NRF_HSFLL_LOCAL
289289

290+
config CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL
291+
bool "NRF IronSide HSFLL LOCAL driver support"
292+
depends on DT_HAS_NORDIC_NRF_IRON_HSFLL_LOCAL_ENABLED
293+
select NRF_IRONSIDE_DVFS_SERVICE
294+
select CLOCK_CONTROL_NRF2_COMMON
295+
default y
296+
297+
if CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL
298+
299+
config CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL_REQ_LOW_FREQ
300+
bool "Local domain scale down after init"
301+
help
302+
Request the lowest operating point after DVFS initialization.
303+
304+
config CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL_DVFS_TIMEOUT_MS
305+
int "Timeout waiting for dvfs request to complete"
306+
default 2000
307+
308+
endif # CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL
309+
290310
config CLOCK_CONTROL_NRF_LFCLK
291311
bool "NRF LFCLK driver support"
292312
depends on DT_HAS_NORDIC_NRF_LFCLK_ENABLED
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#define DT_DRV_COMPAT nordic_nrf_iron_hsfll_local
7+
8+
#include "clock_control_nrf2_common.h"
9+
#include <zephyr/devicetree.h>
10+
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
11+
12+
#include <zephyr/logging/log.h>
13+
LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
14+
15+
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, "multiple instances not supported");
16+
17+
#ifdef CONFIG_NRF_IRONSIDE_DVFS_SERVICE
18+
#include <zephyr/drivers/firmware/nrf_ironside/dvfs.h>
19+
20+
#define HSFLL_FREQ_LOW MHZ(64)
21+
#define HSFLL_FREQ_MEDLOW MHZ(128)
22+
#define HSFLL_FREQ_HIGH MHZ(320)
23+
24+
#define IRONSIDE_DVFS_TIMEOUT K_MSEC(CONFIG_CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL_DVFS_TIMEOUT_MS)
25+
26+
/* Clock options sorted from lowest to highest frequency */
27+
static const struct clock_options {
28+
uint32_t frequency;
29+
enum ironside_dvfs_oppoint setting;
30+
} clock_options[] = {
31+
{
32+
.frequency = HSFLL_FREQ_LOW,
33+
.setting = IRONSIDE_DVFS_OPP_LOW,
34+
},
35+
{
36+
.frequency = HSFLL_FREQ_MEDLOW,
37+
.setting = IRONSIDE_DVFS_OPP_MEDLOW,
38+
},
39+
{
40+
.frequency = HSFLL_FREQ_HIGH,
41+
.setting = IRONSIDE_DVFS_OPP_HIGH,
42+
},
43+
};
44+
45+
struct hsfll_dev_data {
46+
STRUCT_CLOCK_CONFIG(hsfll, ARRAY_SIZE(clock_options)) clk_cfg;
47+
struct k_timer timer;
48+
};
49+
50+
static void hsfll_update_timeout_handler(struct k_timer *timer)
51+
{
52+
struct hsfll_dev_data *dev_data = CONTAINER_OF(timer, struct hsfll_dev_data, timer);
53+
54+
clock_config_update_end(&dev_data->clk_cfg, -ETIMEDOUT);
55+
}
56+
57+
static void hsfll_work_handler(struct k_work *work)
58+
{
59+
struct hsfll_dev_data *dev_data = CONTAINER_OF(work, struct hsfll_dev_data, clk_cfg.work);
60+
enum ironside_dvfs_oppoint required_setting;
61+
uint8_t to_activate_idx;
62+
int rc;
63+
64+
to_activate_idx = clock_config_update_begin(work);
65+
required_setting = clock_options[to_activate_idx].setting;
66+
67+
k_timer_start(&dev_data->timer, IRONSIDE_DVFS_TIMEOUT, K_NO_WAIT);
68+
69+
/* Request the DVFS service to change the OPP point. */
70+
rc = ironside_dvfs_change_oppoint(required_setting);
71+
k_timer_stop(&dev_data->timer);
72+
clock_config_update_end(&dev_data->clk_cfg, rc);
73+
}
74+
75+
static int hsfll_resolve_spec_to_idx(const struct nrf_clock_spec *req_spec)
76+
{
77+
uint32_t req_frequency;
78+
79+
if (req_spec->accuracy || req_spec->precision) {
80+
LOG_ERR("invalid specification of accuracy or precision");
81+
return -EINVAL;
82+
}
83+
84+
req_frequency = req_spec->frequency == NRF_CLOCK_CONTROL_FREQUENCY_MAX
85+
? HSFLL_FREQ_HIGH
86+
: req_spec->frequency;
87+
88+
for (int i = 0; i < ARRAY_SIZE(clock_options); ++i) {
89+
if (req_frequency > clock_options[i].frequency) {
90+
continue;
91+
}
92+
93+
return i;
94+
}
95+
96+
LOG_ERR("invalid frequency");
97+
return -EINVAL;
98+
}
99+
100+
static void hsfll_get_spec_by_idx(uint8_t idx, struct nrf_clock_spec *spec)
101+
{
102+
spec->frequency = clock_options[idx].frequency;
103+
spec->accuracy = 0;
104+
spec->precision = 0;
105+
}
106+
107+
static struct onoff_manager *hsfll_get_mgr_by_idx(const struct device *dev, uint8_t idx)
108+
{
109+
struct hsfll_dev_data *dev_data = dev->data;
110+
111+
return &dev_data->clk_cfg.onoff[idx].mgr;
112+
}
113+
114+
static struct onoff_manager *hsfll_find_mgr_by_spec(const struct device *dev,
115+
const struct nrf_clock_spec *spec)
116+
{
117+
int idx;
118+
119+
if (!spec) {
120+
return hsfll_get_mgr_by_idx(dev, 0);
121+
}
122+
123+
idx = hsfll_resolve_spec_to_idx(spec);
124+
return idx < 0 ? NULL : hsfll_get_mgr_by_idx(dev, idx);
125+
}
126+
#endif /* CONFIG_NRF_IRONSIDE_DVFS_SERVICE */
127+
128+
static int api_request_hsfll(const struct device *dev, const struct nrf_clock_spec *spec,
129+
struct onoff_client *cli)
130+
{
131+
#ifdef CONFIG_NRF_IRONSIDE_DVFS_SERVICE
132+
struct onoff_manager *mgr = hsfll_find_mgr_by_spec(dev, spec);
133+
134+
if (mgr) {
135+
return clock_config_request(mgr, cli);
136+
}
137+
138+
return -EINVAL;
139+
#else
140+
return -ENOTSUP;
141+
#endif
142+
}
143+
144+
static int api_release_hsfll(const struct device *dev, const struct nrf_clock_spec *spec)
145+
{
146+
#ifdef CONFIG_NRF_IRONSIDE_DVFS_SERVICE
147+
struct onoff_manager *mgr = hsfll_find_mgr_by_spec(dev, spec);
148+
149+
if (mgr) {
150+
return onoff_release(mgr);
151+
}
152+
153+
return -EINVAL;
154+
#else
155+
return -ENOTSUP;
156+
#endif
157+
}
158+
159+
static int api_cancel_or_release_hsfll(const struct device *dev, const struct nrf_clock_spec *spec,
160+
struct onoff_client *cli)
161+
{
162+
#ifdef CONFIG_NRF_IRONSIDE_DVFS_SERVICE
163+
struct onoff_manager *mgr = hsfll_find_mgr_by_spec(dev, spec);
164+
165+
if (mgr) {
166+
return onoff_cancel_or_release(mgr, cli);
167+
}
168+
169+
return -EINVAL;
170+
#else
171+
return -ENOTSUP;
172+
#endif
173+
}
174+
175+
static int api_resolve_hsfll(const struct device *dev, const struct nrf_clock_spec *req_spec,
176+
struct nrf_clock_spec *res_spec)
177+
{
178+
#ifdef CONFIG_NRF_IRONSIDE_DVFS_SERVICE
179+
int idx;
180+
181+
idx = hsfll_resolve_spec_to_idx(req_spec);
182+
if (idx < 0) {
183+
return -EINVAL;
184+
}
185+
186+
hsfll_get_spec_by_idx(idx, res_spec);
187+
return 0;
188+
#else
189+
return -ENOTSUP;
190+
#endif
191+
}
192+
193+
static int hsfll_init(const struct device *dev)
194+
{
195+
#ifdef CONFIG_NRF_IRONSIDE_DVFS_SERVICE
196+
struct hsfll_dev_data *dev_data = dev->data;
197+
int rc;
198+
199+
rc = clock_config_init(&dev_data->clk_cfg, ARRAY_SIZE(dev_data->clk_cfg.onoff),
200+
hsfll_work_handler);
201+
if (rc < 0) {
202+
return rc;
203+
}
204+
205+
k_timer_init(&dev_data->timer, hsfll_update_timeout_handler, NULL);
206+
207+
#endif
208+
209+
return 0;
210+
}
211+
212+
static DEVICE_API(nrf_clock_control, hsfll_drv_api) = {
213+
.std_api =
214+
{
215+
.on = api_nosys_on_off,
216+
.off = api_nosys_on_off,
217+
},
218+
.request = api_request_hsfll,
219+
.release = api_release_hsfll,
220+
.cancel_or_release = api_cancel_or_release_hsfll,
221+
.resolve = api_resolve_hsfll,
222+
};
223+
224+
#ifdef CONFIG_NRF_IRONSIDE_DVFS_SERVICE
225+
static struct hsfll_dev_data hsfll_data;
226+
#endif
227+
228+
#ifdef CONFIG_CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL_REQ_LOW_FREQ
229+
static int dvfs_low_init(void)
230+
{
231+
static const k_timeout_t timeout = IRONSIDE_DVFS_TIMEOUT;
232+
static const struct device *hsfll_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_NODELABEL(cpu)));
233+
static const struct nrf_clock_spec clk_spec = {.frequency = HSFLL_FREQ_LOW};
234+
235+
return nrf_clock_control_request_sync(hsfll_dev, &clk_spec, timeout);
236+
}
237+
238+
SYS_INIT(dvfs_low_init, APPLICATION, 0);
239+
#endif
240+
241+
DEVICE_DT_INST_DEFINE(0, hsfll_init, NULL,
242+
COND_CODE_1(CONFIG_NRF_IRONSIDE_DVFS_SERVICE,
243+
(&hsfll_data),
244+
(NULL)), NULL, PRE_KERNEL_1,
245+
CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &hsfll_drv_api);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright (c) 2025 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: |
5+
Nordic nRF local HSFLL with IronSide DVFS support
6+
7+
The local HSFLL mixed-mode IP generates several clock frequencies in the range
8+
from 64 MHz to 400 MHz (in steps of 16 MHz).
9+
10+
Usage example:
11+
12+
hsfll: clock@deadbeef {
13+
compatible = "nordic,nrf-hsfll-local";
14+
reg = <0xdeadbeef 0x1000>;
15+
clocks = <&fll16m>;
16+
clock-frequency = <DT_FREQ_M(320)>;
17+
nordic,ficrs = <&ficr NRF_FICR_TRIM_APPLICATION_HSFLL_TRIM_VSUP>,
18+
<&ficr NRF_FICR_TRIM_APPLICATION_HSFLL_TRIM_COARSE_0>,
19+
<&ficr NRF_FICR_TRIM_APPLICATION_HSFLL_TRIM_FINE_0>;
20+
nordic,ficr-names = "vsup", "coarse", "fine";
21+
};
22+
23+
Required FICR entries are for VSUP, COARSE and FINE trim values.
24+
25+
compatible: "nordic,nrf-iron-hsfll-local"
26+
27+
include: [base.yaml, fixed-clock.yaml, nordic-nrf-ficr-client.yaml]
28+
29+
properties:
30+
reg:
31+
required: true
32+
33+
clocks:
34+
required: true
35+
36+
clock-frequency:
37+
enum:
38+
- 64000000
39+
- 80000000
40+
- 96000000
41+
- 112000000
42+
- 128000000
43+
- 144000000
44+
- 160000000
45+
- 176000000
46+
- 192000000
47+
- 208000000
48+
- 224000000
49+
- 240000000
50+
- 256000000
51+
- 272000000
52+
- 288000000
53+
- 304000000
54+
- 320000000
55+
- 336000000
56+
- 352000000
57+
- 368000000
58+
- 384000000
59+
- 400000000

0 commit comments

Comments
 (0)