1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #define DT_DRV_COMPAT nordic_nrf_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 /* TODO: add support for other HSFLLs */
16 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
17 "multiple instances not supported");
18
19 #ifdef CONFIG_NRFS_DVFS_LOCAL_DOMAIN
20 #include <ld_dvfs_handler.h>
21
22 #define FLAG_FREQ_CHANGE_CB_EXPECTED BIT(FLAGS_COMMON_BITS)
23
24 #define HSFLL_FREQ_LOW MHZ(64)
25 #define HSFLL_FREQ_MEDLOW MHZ(128)
26 #define HSFLL_FREQ_HIGH MHZ(320)
27
28 #define NRFS_DVFS_TIMEOUT K_MSEC(CONFIG_CLOCK_CONTROL_NRF2_NRFS_DVFS_TIMEOUT_MS)
29
30 /* Clock options sorted from lowest to highest frequency */
31 static const struct clock_options {
32 uint32_t frequency;
33 enum dvfs_frequency_setting setting;
34 } clock_options[] = {
35 {
36 .frequency = HSFLL_FREQ_LOW,
37 .setting = DVFS_FREQ_LOW,
38 },
39 {
40 .frequency = HSFLL_FREQ_MEDLOW,
41 .setting = DVFS_FREQ_MEDLOW,
42 },
43 {
44 .frequency = HSFLL_FREQ_HIGH,
45 .setting = DVFS_FREQ_HIGH,
46 },
47 };
48
49 struct hsfll_dev_data {
50 STRUCT_CLOCK_CONFIG(hsfll, ARRAY_SIZE(clock_options)) clk_cfg;
51 struct k_timer timer;
52 };
53
freq_setting_applied_cb(enum dvfs_frequency_setting new_setting)54 static void freq_setting_applied_cb(enum dvfs_frequency_setting new_setting)
55 {
56 ARG_UNUSED(new_setting);
57
58 const struct device *dev = DEVICE_DT_INST_GET(0);
59 struct hsfll_dev_data *dev_data = dev->data;
60 atomic_val_t prev_flags;
61
62 /* Process only expected notifications (after sent requests). */
63 prev_flags = atomic_and(&dev_data->clk_cfg.flags,
64 ~FLAG_FREQ_CHANGE_CB_EXPECTED);
65 if (prev_flags & FLAG_FREQ_CHANGE_CB_EXPECTED) {
66 k_timer_stop(&dev_data->timer);
67
68 clock_config_update_end(&dev_data->clk_cfg, 0);
69 }
70 }
71
hsfll_update_timeout_handler(struct k_timer * timer)72 static void hsfll_update_timeout_handler(struct k_timer *timer)
73 {
74 struct hsfll_dev_data *dev_data =
75 CONTAINER_OF(timer, struct hsfll_dev_data, timer);
76
77 clock_config_update_end(&dev_data->clk_cfg, -ETIMEDOUT);
78 }
79
hsfll_work_handler(struct k_work * work)80 static void hsfll_work_handler(struct k_work *work)
81 {
82 struct hsfll_dev_data *dev_data =
83 CONTAINER_OF(work, struct hsfll_dev_data, clk_cfg.work);
84 enum dvfs_frequency_setting required_setting;
85 uint8_t to_activate_idx;
86 int rc;
87
88 to_activate_idx = clock_config_update_begin(work);
89 required_setting = clock_options[to_activate_idx].setting;
90
91 /* Notify the DVFS service about the required setting. */
92 rc = dvfs_service_handler_change_freq_setting(required_setting);
93 if (rc < 0) {
94 clock_config_update_end(&dev_data->clk_cfg, rc);
95 return;
96 }
97
98 /* And expect a confirmation that the setting is ready to be used. */
99 (void)atomic_or(&dev_data->clk_cfg.flags, FLAG_FREQ_CHANGE_CB_EXPECTED);
100 k_timer_start(&dev_data->timer, NRFS_DVFS_TIMEOUT, K_NO_WAIT);
101 }
102
hsfll_find_mgr(const struct device * dev,const struct nrf_clock_spec * spec)103 static struct onoff_manager *hsfll_find_mgr(const struct device *dev,
104 const struct nrf_clock_spec *spec)
105 {
106 struct hsfll_dev_data *dev_data = dev->data;
107 uint32_t frequency;
108
109 if (!spec) {
110 return &dev_data->clk_cfg.onoff[0].mgr;
111 }
112
113 if (spec->accuracy || spec->precision) {
114 LOG_ERR("invalid specification of accuracy or precision");
115 return NULL;
116 }
117
118 frequency = spec->frequency == NRF_CLOCK_CONTROL_FREQUENCY_MAX
119 ? HSFLL_FREQ_HIGH
120 : spec->frequency;
121
122 for (int i = 0; i < ARRAY_SIZE(clock_options); ++i) {
123 if (frequency > clock_options[i].frequency) {
124 continue;
125 }
126
127 return &dev_data->clk_cfg.onoff[i].mgr;
128 }
129
130 LOG_ERR("invalid frequency");
131 return NULL;
132 }
133 #endif /* CONFIG_NRFS_DVFS_LOCAL_DOMAIN */
134
api_request_hsfll(const struct device * dev,const struct nrf_clock_spec * spec,struct onoff_client * cli)135 static int api_request_hsfll(const struct device *dev,
136 const struct nrf_clock_spec *spec,
137 struct onoff_client *cli)
138 {
139 #ifdef CONFIG_NRFS_DVFS_LOCAL_DOMAIN
140 struct onoff_manager *mgr = hsfll_find_mgr(dev, spec);
141
142 if (mgr) {
143 return onoff_request(mgr, cli);
144 }
145
146 return -EINVAL;
147 #else
148 return -ENOTSUP;
149 #endif
150 }
151
api_release_hsfll(const struct device * dev,const struct nrf_clock_spec * spec)152 static int api_release_hsfll(const struct device *dev,
153 const struct nrf_clock_spec *spec)
154 {
155 #ifdef CONFIG_NRFS_DVFS_LOCAL_DOMAIN
156 struct onoff_manager *mgr = hsfll_find_mgr(dev, spec);
157
158 if (mgr) {
159 return onoff_release(mgr);
160 }
161
162 return -EINVAL;
163 #else
164 return -ENOTSUP;
165 #endif
166 }
167
api_cancel_or_release_hsfll(const struct device * dev,const struct nrf_clock_spec * spec,struct onoff_client * cli)168 static int api_cancel_or_release_hsfll(const struct device *dev,
169 const struct nrf_clock_spec *spec,
170 struct onoff_client *cli)
171 {
172 #ifdef CONFIG_NRFS_DVFS_LOCAL_DOMAIN
173 struct onoff_manager *mgr = hsfll_find_mgr(dev, spec);
174
175 if (mgr) {
176 return onoff_cancel_or_release(mgr, cli);
177 }
178
179 return -EINVAL;
180 #else
181 return -ENOTSUP;
182 #endif
183 }
184
hsfll_init(const struct device * dev)185 static int hsfll_init(const struct device *dev)
186 {
187 #ifdef CONFIG_NRFS_DVFS_LOCAL_DOMAIN
188 struct hsfll_dev_data *dev_data = dev->data;
189 int rc;
190
191 rc = clock_config_init(&dev_data->clk_cfg,
192 ARRAY_SIZE(dev_data->clk_cfg.onoff),
193 hsfll_work_handler);
194 if (rc < 0) {
195 return rc;
196 }
197
198 k_timer_init(&dev_data->timer, hsfll_update_timeout_handler, NULL);
199
200 dvfs_service_handler_register_freq_setting_applied_callback(
201 freq_setting_applied_cb);
202 #endif
203
204 return 0;
205 }
206
207 static DEVICE_API(nrf_clock_control, hsfll_drv_api) = {
208 .std_api = {
209 .on = api_nosys_on_off,
210 .off = api_nosys_on_off,
211 },
212 .request = api_request_hsfll,
213 .release = api_release_hsfll,
214 .cancel_or_release = api_cancel_or_release_hsfll,
215 };
216
217 #ifdef CONFIG_NRFS_DVFS_LOCAL_DOMAIN
218 static struct hsfll_dev_data hsfll_data;
219 #endif
220
221 DEVICE_DT_INST_DEFINE(0, hsfll_init, NULL,
222 COND_CODE_1(CONFIG_NRFS_DVFS_LOCAL_DOMAIN,
223 (&hsfll_data),
224 (NULL)),
225 NULL,
226 PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
227 &hsfll_drv_api);
228