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