1616#include <zephyr/logging/log.h>
1717LOG_MODULE_REGISTER (MODULE , CONFIG_DESKTOP_DVFS_LOG_LEVEL );
1818
19- #include <ld_dvfs_handler.h>
19+ #include <zephyr/drivers/clock_control/nrf_clock_control.h>
20+ #include <zephyr/devicetree.h>
21+
22+ #define CLOCK_NODE DT_ALIAS(nrfdesktop_dvfs_clock)
23+
24+ #if !DT_NODE_HAS_STATUS (CLOCK_NODE , okay )
25+ #error "Alias 'nrfdesktop-dvfs-clock' is not defined in the device tree!"
26+ #endif
27+
28+ static const struct device * dvfs_clock_dev = DEVICE_DT_GET (CLOCK_NODE );
29+ static struct onoff_client cli ;
30+
31+ /* The used nrf_clock_control driver implementation does not support
32+ * clock precision and clock accuracy ppm.
33+ */
34+ #define DESKTOP_DVFS_CLOCK_PRECISION 0
35+ #define DESKTOP_DVFS_CLOCK_ACCURACY_PPM 0
36+
37+ static struct nrf_clock_spec spec = {
38+ .accuracy = DESKTOP_DVFS_CLOCK_ACCURACY_PPM ,
39+ .precision = DESKTOP_DVFS_CLOCK_PRECISION ,
40+ };
2041
2142#define DVFS_RETRY_INITIALIZATION_TIMEOUT K_MSEC(CONFIG_DESKTOP_DVFS_RETRY_INIT_TIMEOUT_MS)
2243#define DVFS_RETRY_BUSY_TIMEOUT K_MSEC(CONFIG_DESKTOP_DVFS_RETRY_BUSY_TIMEOUT_MS)
@@ -66,16 +87,33 @@ enum dvfs_state {
6687 LIST_FOR_DVFS_STATE (DVFS_STATE )
6788};
6889
69- static const uint8_t dvfs_high_freq_bitmask = INITIALIZE_DVFS_FREQ_MASK (ACTIVE_FREQ_HIGH );
70- static const uint8_t dvfs_medlow_freq_bitmask = INITIALIZE_DVFS_FREQ_MASK (ACTIVE_FREQ_MEDLOW );
90+ struct dvfs_frequency {
91+ uint32_t freq ;
92+ uint8_t bitmask ;
93+ };
94+
95+ static const struct dvfs_frequency dvfs_freq_array [] = {
96+ {
97+ .freq = CONFIG_DESKTOP_DVFS_FREQ_HIGH ,
98+ .bitmask = INITIALIZE_DVFS_FREQ_MASK (ACTIVE_FREQ_HIGH )
99+ },
100+ {
101+ .freq = CONFIG_DESKTOP_DVFS_FREQ_MED ,
102+ .bitmask = INITIALIZE_DVFS_FREQ_MASK (ACTIVE_FREQ_MEDLOW )
103+ },
104+ {
105+ .freq = CONFIG_DESKTOP_DVFS_FREQ_LOW ,
106+ .bitmask = UINT8_MAX
107+ },
108+ };
71109
72110/* Binary mask tracking which states are requested. */
73111static uint8_t dfvs_requests_state_bitmask ;
74112
75- static enum dvfs_frequency_setting current_freq = DVFS_FREQ_HIGH ;
113+ /* SoC starts with 320MHz frequency */
114+ static uint32_t current_freq = dvfs_freq_array [0 ].freq ;
76115
77- BUILD_ASSERT (sizeof (dvfs_high_freq_bitmask ) == sizeof (dfvs_requests_state_bitmask ));
78- BUILD_ASSERT (sizeof (dvfs_medlow_freq_bitmask ) == sizeof (dfvs_requests_state_bitmask ));
116+ BUILD_ASSERT (sizeof (dvfs_freq_array [0 ].bitmask ) == sizeof (dfvs_requests_state_bitmask ));
79117BUILD_ASSERT (CHAR_BIT * sizeof (dfvs_requests_state_bitmask ) >= DVFS_STATE_COUNT );
80118
81119static struct dvfs_retry {
@@ -88,20 +126,14 @@ struct dvfs_state_timeout {
88126 uint16_t timeout_ms ;
89127};
90128
129+ static struct k_work dvfs_notify_work ;
130+ static uint32_t requested_freq ;
131+ static bool request_in_progress ;
132+
91133static struct dvfs_state_timeout dvfs_state_timeouts [DVFS_STATE_COUNT ] = {
92134 LIST_FOR_DVFS_STATE (INITIALIZE_DVFS_STATE_TIMEOUT )
93135};
94136
95- static const char * get_dvfs_frequency_setting_name (enum dvfs_frequency_setting setting )
96- {
97- switch (setting ) {
98- case DVFS_FREQ_HIGH : return "DVFS_FREQ_HIGH" ;
99- case DVFS_FREQ_MEDLOW : return "DVFS_FREQ_MEDLOW" ;
100- case DVFS_FREQ_LOW : return "DVFS_FREQ_LOW" ;
101- default : return "Unknown" ;
102- }
103- }
104-
105137static const char * get_dvfs_state_name (enum dvfs_state state )
106138{
107139 switch (state ) {
@@ -111,19 +143,21 @@ static const char *get_dvfs_state_name(enum dvfs_state state)
111143 }
112144}
113145
114- static void cancel_dvfs_retry_work (void )
146+ static void dvfs_fatal_error (void )
115147{
148+ module_set_state (MODULE_STATE_ERROR );
149+ module_state = STATE_ERROR ;
116150 (void ) k_work_cancel_delayable (& dvfs_retry .retry_work );
117- dvfs_retry .retries_cnt = 0 ;
151+ for (size_t i = 0 ; i < ARRAY_SIZE (dvfs_state_timeouts ); i ++ ) {
152+ (void ) k_work_cancel_delayable (& dvfs_state_timeouts [i ].timeout_work );
153+ }
118154}
119155
120156static void handle_dvfs_error (int32_t err )
121157{
122158 if (dvfs_retry .retries_cnt >= DVFS_NUMBER_OF_RETRIES ) {
123159 LOG_ERR ("DVFS retry count exceeded." );
124- module_set_state (MODULE_STATE_ERROR );
125- module_state = STATE_ERROR ;
126- cancel_dvfs_retry_work ();
160+ dvfs_fatal_error ();
127161 return ;
128162 }
129163 dvfs_retry .retries_cnt ++ ;
@@ -137,37 +171,113 @@ static void handle_dvfs_error(int32_t err)
137171 timeout = DVFS_RETRY_INITIALIZATION_TIMEOUT ;
138172 } else {
139173 LOG_ERR ("DVFS freq change returned with error: %d" , err );
140- module_set_state (MODULE_STATE_ERROR );
141- module_state = STATE_ERROR ;
142- cancel_dvfs_retry_work ();
174+ dvfs_fatal_error ();
143175 return ;
144176 }
145177 (void ) k_work_reschedule (& dvfs_retry .retry_work , timeout );
146178}
147179
148- static void set_dvfs_freq ( enum dvfs_frequency_setting target_freq )
180+ static uint32_t check_required_frequency ( void )
149181{
150- int32_t ret = dvfs_service_handler_change_freq_setting (target_freq );
182+ for (size_t i = 0 ; i < ARRAY_SIZE (dvfs_freq_array ) - 1 ; i ++ ) {
183+ if (dfvs_requests_state_bitmask & dvfs_freq_array [i ].bitmask ) {
184+ return dvfs_freq_array [i ].freq ;
185+ }
186+ }
187+
188+ /* If no state is active, return the lowest frequency. */
189+ return dvfs_freq_array [ARRAY_SIZE (dvfs_freq_array ) - 1 ].freq ;
190+ }
191+
192+ static void dvfs_notify_cb (struct onoff_manager * srv ,
193+ struct onoff_client * cli ,
194+ uint32_t state ,
195+ int res )
196+ {
197+ (void ) k_work_submit (& dvfs_notify_work );
198+ }
199+
200+ static void set_dvfs_freq (uint32_t target_freq )
201+ {
202+ int ret ;
203+
204+ if (spec .frequency != 0 ) {
205+ ret = nrf_clock_control_release (dvfs_clock_dev , & spec );
206+ if (ret < 0 ) {
207+ LOG_ERR ("Failed to release requested clock specs, error: %d" , ret );
208+ dvfs_fatal_error ();
209+ return ;
210+ }
211+ }
212+
213+ sys_notify_init_callback (& cli .notify , dvfs_notify_cb );
151214
215+ spec .frequency = target_freq ;
216+ ret = nrf_clock_control_request (dvfs_clock_dev , & spec , & cli );
152217 if (ret ) {
153218 handle_dvfs_error (ret );
154219 } else {
155- current_freq = target_freq ;
156- LOG_INF ("Have requested %s frequency" ,
157- get_dvfs_frequency_setting_name (target_freq ));
158- cancel_dvfs_retry_work ();
220+ LOG_INF ("Have requested %" PRIu32 " frequency" , target_freq );
221+ requested_freq = target_freq ;
222+ request_in_progress = true;
159223 }
160224}
161225
162- static enum dvfs_frequency_setting check_required_frequency (void )
226+ static void dvfs_frequency_update (void )
163227{
164- if (dfvs_requests_state_bitmask & dvfs_high_freq_bitmask ) {
165- return DVFS_FREQ_HIGH ;
166- } else if (dfvs_requests_state_bitmask & dvfs_medlow_freq_bitmask ) {
167- return DVFS_FREQ_MEDLOW ;
168- } else {
169- return DVFS_FREQ_LOW ;
228+ uint32_t required_freq = check_required_frequency ();
229+
230+ if ((!k_work_delayable_is_pending (& dvfs_retry .retry_work )) &&
231+ !request_in_progress && (required_freq != current_freq )) {
232+ set_dvfs_freq (required_freq );
233+ } else if ((required_freq == current_freq ) &&
234+ (k_work_delayable_is_pending (& dvfs_retry .retry_work ))) {
235+ (void ) k_work_cancel_delayable (& dvfs_retry .retry_work );
236+ /* retry_cnt should be only cleared on successful frequency change. */
237+ }
238+ }
239+
240+ static void dvfs_notify_work_handler (struct k_work * work )
241+ {
242+ int res ;
243+ int ret = sys_notify_fetch_result (& cli .notify , & res );
244+
245+ if (ret < 0 ) {
246+ LOG_ERR ("Work not completed, verify usage of notify API" );
247+ dvfs_fatal_error ();
248+ return ;
249+ }
250+
251+ __ASSERT_NO_MSG (request_in_progress );
252+ request_in_progress = false;
253+
254+ if (res < 0 ) {
255+ handle_dvfs_error (res );
256+ return ;
257+ }
258+ ret = clock_control_get_rate (dvfs_clock_dev , NULL , & current_freq );
259+ if (ret < 0 ) {
260+ LOG_ERR ("Failed to get current frequency with error: %d" , ret );
261+ dvfs_fatal_error ();
262+ return ;
263+ }
264+ if (requested_freq != current_freq ) {
265+ /*
266+ * In current solution it is assumed that no other module
267+ * will change cpu frequency.
268+ */
269+ LOG_ERR ("Requested frequency %" PRIu32
270+ " is not the same as current frequency %" PRIu32 ,
271+ requested_freq , current_freq );
272+ dvfs_fatal_error ();
273+ return ;
170274 }
275+
276+ dvfs_retry .retries_cnt = 0 ;
277+ LOG_INF ("DVFS completed, current frequency is: %" PRIu32 , current_freq );
278+
279+ /* Check if there were any new requests. */
280+ dvfs_frequency_update ();
171281}
172282
173283static void process_dvfs_states (enum dvfs_state state , bool turn_on )
@@ -184,15 +294,7 @@ static void process_dvfs_states(enum dvfs_state state, bool turn_on)
184294 LOG_DBG ("%s NOT ACTIVE" , get_dvfs_state_name (state ));
185295 }
186296
187- enum dvfs_frequency_setting required_freq = check_required_frequency ();
188-
189- if ((required_freq != current_freq ) &&
190- (!k_work_delayable_is_pending (& dvfs_retry .retry_work ))) {
191- set_dvfs_freq (required_freq );
192- } else if ((required_freq == current_freq ) &&
193- (k_work_delayable_is_pending (& dvfs_retry .retry_work ))) {
194- cancel_dvfs_retry_work ();
195- }
297+ dvfs_frequency_update ();
196298}
197299
198300static bool handle_ble_peer_conn_params_event (const struct ble_peer_conn_params_event * event )
@@ -226,10 +328,9 @@ static void dvfs_retry_work_handler(struct k_work *work)
226328{
227329 LOG_DBG ("Retrying to change DVFS frequency." );
228330
229- enum dvfs_frequency_setting required_freq = check_required_frequency ();
331+ uint32_t required_freq = check_required_frequency ();
230332
231333 __ASSERT_NO_MSG (required_freq != current_freq );
232-
233334 set_dvfs_freq (required_freq );
234335}
235336
@@ -252,27 +353,37 @@ static bool app_event_handler(const struct app_event_header *aeh)
252353 const struct module_state_event * event = cast_module_state_event (aeh );
253354
254355 if (check_state (event , MODULE_ID (main ), MODULE_STATE_READY )) {
255- __ASSERT_NO_MSG ((dvfs_high_freq_bitmask & dvfs_medlow_freq_bitmask ) == 0 );
356+ BUILD_ASSERT (ARRAY_SIZE (dvfs_freq_array ) == 3 ,
357+ "Add asserts if frequency array is extended" );
358+ __ASSERT_NO_MSG ((dvfs_freq_array [0 ].bitmask &
359+ dvfs_freq_array [1 ].bitmask ) == 0 );
256360
257- k_work_init_delayable ( & dvfs_retry . retry_work ,
258- dvfs_retry_work_handler );
361+ k_work_init ( & dvfs_notify_work , dvfs_notify_work_handler );
362+ k_work_init_delayable ( & dvfs_retry . retry_work , dvfs_retry_work_handler );
259363 for (size_t i = 0 ; i < ARRAY_SIZE (dvfs_state_timeouts ); i ++ ) {
260364 k_work_init_delayable (& dvfs_state_timeouts [i ].timeout_work ,
261365 dvfs_state_timeout_work_handler );
262366 }
367+ int ret = clock_control_get_rate (dvfs_clock_dev ,
368+ NULL , & current_freq );
369+
370+ if (ret < 0 ) {
371+ LOG_ERR ("Failed to get current frequency with error: %d" ,
372+ ret );
373+ dvfs_fatal_error ();
374+ return false;
375+ }
263376 module_state = STATE_READY ;
264377
265378 if (IS_ENABLED (CONFIG_DESKTOP_DVFS_STATE_INITIALIZING_ENABLE )) {
266- if (module_flags_check_zero (& req_modules_bm )) {
379+ get_req_modules (& req_modules_bm );
380+ if (!module_flags_check_zero (& req_modules_bm )) {
267381 process_dvfs_states (DVFS_STATE_INITIALIZING , true);
382+ } else {
383+ dvfs_frequency_update ();
268384 }
269- get_req_modules (& req_modules_bm );
270385 } else {
271- enum dvfs_frequency_setting required_freq =
272- check_required_frequency ();
273- if (required_freq != current_freq ) {
274- set_dvfs_freq (required_freq );
275- }
386+ dvfs_frequency_update ();
276387 }
277388 }
278389
0 commit comments