Software APIs
dif_clkmgr.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
6
7#include <assert.h>
8
12#include "sw/device/lib/base/multibits.h"
14
15#include "hw/top/clkmgr_regs.h" // Generated
16
17// TODO: For the moment, CLKMGR_PARAM_NUM_SW_GATEABLE_CLOCKS has to be <= than
18// 32, as we only support one enable register for gateable clocks.
19// https://github.com/lowRISC/opentitan/issues/4201
20static_assert(
21 CLKMGR_PARAM_NUM_SW_GATEABLE_CLOCKS <= CLKMGR_PARAM_REG_WIDTH,
22 "Expected the number of gateable clocks to be <= the width of a CSR.");
23
24// TODO: For the moment, CLKMGR_PARAM_NUM_HINTABLE_CLOCKS has to be <= than
25// 32, as we only support one enable/hint_status register for hintable clocks.
26// https://github.com/lowRISC/opentitan/issues/4201
27static_assert(
28 CLKMGR_PARAM_NUM_HINTABLE_CLOCKS <= CLKMGR_PARAM_REG_WIDTH,
29 "Expected the number of hintable clocks to be <= the width of a CSR.");
30
31static bool clkmgr_valid_gateable_clock(dif_clkmgr_gateable_clock_t clock) {
32 return clock < CLKMGR_PARAM_NUM_SW_GATEABLE_CLOCKS;
33}
34
35static bool clkmgr_valid_hintable_clock(dif_clkmgr_hintable_clock_t clock) {
36 return clock < CLKMGR_PARAM_NUM_HINTABLE_CLOCKS;
37}
38
39static bool clkmgr_measure_ctrl_regwen(const dif_clkmgr_t *clkmgr) {
40 uint32_t measure_ctrl_regwen_val = mmio_region_read32(
41 clkmgr->base_addr, CLKMGR_MEASURE_CTRL_REGWEN_REG_OFFSET);
42 return bitfield_bit32_read(measure_ctrl_regwen_val,
43 CLKMGR_MEASURE_CTRL_REGWEN_EN_BIT);
44}
45
46/**
47 * Checks if the jitter enable register is locked.
48 *
49 * The jitter enable register is locked by CLKMGR_JITTER_REGWEN.
50 */
52static bool jitter_enable_register_is_locked(const dif_clkmgr_t *clkmgr) {
53 // Jitter enable register is locked when `CLKMGR_JITTER_REGWEN_EN_BIT` bit
54 // is 0.
55 return !bitfield_bit32_read(
56 mmio_region_read32(clkmgr->base_addr, CLKMGR_JITTER_REGWEN_REG_OFFSET),
57 CLKMGR_JITTER_REGWEN_EN_BIT);
58}
59
60#if OPENTITAN_CLKMGR_HAS_SW_EXTCLK_REGWEN
61/**
62 * Checks if the external clock control register is locked.
63 *
64 * The external clock control register is locked by CLKMGR_EXTCLK_CTRL_REGWEN.
65 */
67static bool extclk_control_register_is_locked(const dif_clkmgr_t *clkmgr) {
68 // External clock control register is locked when
69 // `CLKMGR_EXTCLK_CTRL_REGWEN_EN_BIT` bit is 0.
70 return !bitfield_bit32_read(
71 mmio_region_read32(clkmgr->base_addr,
72 CLKMGR_EXTCLK_CTRL_REGWEN_REG_OFFSET),
73 CLKMGR_EXTCLK_CTRL_REGWEN_EN_BIT);
74}
75
76dif_result_t dif_clkmgr_external_clock_is_settled(const dif_clkmgr_t *clkmgr,
77 bool *status) {
78 if (clkmgr == NULL || status == NULL) {
79 return kDifBadArg;
80 }
81 uint32_t extclk_status_val =
82 mmio_region_read32(clkmgr->base_addr, CLKMGR_EXTCLK_STATUS_REG_OFFSET);
83 *status = bitfield_field32_read(extclk_status_val,
84 CLKMGR_EXTCLK_STATUS_ACK_FIELD) ==
85 kMultiBitBool4True;
86
87 return kDifOk;
88}
89#endif
90
91dif_result_t dif_clkmgr_jitter_enable_is_locked(const dif_clkmgr_t *clkmgr,
92 bool *is_locked) {
93 if (clkmgr == NULL || is_locked == NULL) {
94 return kDifBadArg;
95 }
96
97 *is_locked = jitter_enable_register_is_locked(clkmgr);
98
99 return kDifOk;
100}
101
102dif_result_t dif_clkmgr_lock_jitter_enable(const dif_clkmgr_t *clkmgr) {
103 if (clkmgr == NULL) {
104 return kDifBadArg;
105 }
106 mmio_region_write32(clkmgr->base_addr, CLKMGR_JITTER_REGWEN_REG_OFFSET, 0);
107 return kDifOk;
108}
109
110dif_result_t dif_clkmgr_jitter_get_enabled(const dif_clkmgr_t *clkmgr,
111 dif_toggle_t *state) {
112 if (clkmgr == NULL || state == NULL) {
113 return kDifBadArg;
114 }
115
116 multi_bit_bool_t clk_jitter_val =
117 mmio_region_read32(clkmgr->base_addr, CLKMGR_JITTER_ENABLE_REG_OFFSET);
118 // The documentation states that kMultiBitBool4False disables the jittery
119 // clock and all other values enable the jittery clock.
120 *state = clk_jitter_val != kMultiBitBool4False ? kDifToggleEnabled
122
123 return kDifOk;
124}
125
126dif_result_t dif_clkmgr_jitter_set_enabled(const dif_clkmgr_t *clkmgr,
127 dif_toggle_t new_state) {
128 multi_bit_bool_t new_jitter_enable_val;
129 if (clkmgr == NULL) {
130 return kDifBadArg;
131 }
132 if (jitter_enable_register_is_locked(clkmgr)) {
133 return kDifLocked;
134 }
135
136 switch (new_state) {
138 new_jitter_enable_val = kMultiBitBool4True;
139 break;
141 new_jitter_enable_val = kMultiBitBool4False;
142 break;
143 default:
144 return kDifBadArg;
145 }
146 mmio_region_write32(clkmgr->base_addr, CLKMGR_JITTER_ENABLE_REG_OFFSET,
147 new_jitter_enable_val);
148 return kDifOk;
149}
150
152dif_result_t dif_clkmgr_find_gateable_clock(
153 const dif_clkmgr_t *clkmgr, dt_instance_id_t inst_id,
155 if (clkmgr == NULL || clock == NULL) {
156 return kDifBadArg;
157 }
158 dt_clkmgr_t dt;
159 DIF_RETURN_IF_ERROR(dif_clkmgr_get_dt(clkmgr, &dt));
160 // Query the DT to find the information.
161 for (size_t i = 0; i < dt_clkmgr_gateable_clock_count(dt); i++) {
162 if (dt_clkmgr_gateable_clock(dt, i) == inst_id) {
163 *clock = i;
164 return kDifOk;
165 }
166 }
167 return kDifError;
168}
169
170dif_result_t dif_clkmgr_gateable_clock_get_enabled(
171 const dif_clkmgr_t *clkmgr, dif_clkmgr_gateable_clock_t clock,
172 dif_toggle_t *state) {
173 if (clkmgr == NULL || state == NULL || !clkmgr_valid_gateable_clock(clock)) {
174 return kDifBadArg;
175 }
176
177#if CLKMGR_PARAM_NUM_SW_GATEABLE_CLOCKS == 0
179#else
180 // NOTE: the code below fails to compile on tops with no gateable clocks,
181 // where CLKMGR_CLK_ENABLES_REG_OFFSET is not defined.
182 uint32_t clk_enables_val =
183 mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET);
184 *state = dif_bool_to_toggle(bitfield_bit32_read(clk_enables_val, clock));
185
186 return kDifOk;
187#endif
188}
189
190dif_result_t dif_clkmgr_gateable_clock_set_enabled(
191 const dif_clkmgr_t *clkmgr, dif_clkmgr_gateable_clock_t clock,
192 dif_toggle_t new_state) {
193 if (clkmgr == NULL || !clkmgr_valid_gateable_clock(clock) ||
194 !dif_is_valid_toggle(new_state)) {
195 return kDifBadArg;
196 }
197
198#if CLKMGR_PARAM_NUM_SW_GATEABLE_CLOCKS == 0
200#else
201 // NOTE: the code below fails to compile on tops with no gateable clocks,
202 // where CLKMGR_CLK_ENABLES_REG_OFFSET is not defined.
203 bool new_clk_enables_bit = dif_toggle_to_bool(new_state);
204 uint32_t clk_enables_val =
205 mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET);
206 clk_enables_val =
207 bitfield_bit32_write(clk_enables_val, clock, new_clk_enables_bit);
208 mmio_region_write32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET,
209 clk_enables_val);
210
211 return kDifOk;
212#endif
213}
214
216dif_result_t dif_clkmgr_find_hintable_clock(
217 const dif_clkmgr_t *clkmgr, dt_instance_id_t inst_id,
219 if (clkmgr == NULL || clock == NULL) {
220 return kDifBadArg;
221 }
222 dt_clkmgr_t dt;
223 DIF_RETURN_IF_ERROR(dif_clkmgr_get_dt(clkmgr, &dt));
224 // Query the DT to find the information.
225 for (size_t i = 0; i < dt_clkmgr_hintable_clock_count(dt); i++) {
226 if (dt_clkmgr_hintable_clock(dt, i) == inst_id) {
227 *clock = i;
228 return kDifOk;
229 }
230 }
231 return kDifError;
232}
233
234dif_result_t dif_clkmgr_hintable_clock_get_enabled(
235 const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
236 dif_toggle_t *state) {
237 if (clkmgr == NULL || state == NULL || !clkmgr_valid_hintable_clock(clock)) {
238 return kDifBadArg;
239 }
240
241#if CLKMGR_PARAM_NUM_HINTABLE_CLOCKS == 0
243#else
244 // NOTE: the code below fails to compile on tops with no hintable clocks,
245 // where CLKMGR_CLK_HINTS_STATUS_REG_OFFSET is not defined.
246 uint32_t clk_hints_val =
247 mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_STATUS_REG_OFFSET);
248 *state = dif_bool_to_toggle(bitfield_bit32_read(clk_hints_val, clock));
249
250 return kDifOk;
251#endif
252}
253
254dif_result_t dif_clkmgr_hintable_clock_set_hint(
255 const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
256 dif_toggle_t new_state) {
257 if (clkmgr == NULL || !clkmgr_valid_hintable_clock(clock) ||
258 !dif_is_valid_toggle(new_state)) {
259 return kDifBadArg;
260 }
261
262#if CLKMGR_PARAM_NUM_HINTABLE_CLOCKS == 0
264#else
265 // NOTE: the code below fails to compile on tops with no hintable clocks,
266 // where CLKMGR_CLK_HINTS_REG_OFFSET is not defined.
267 bool new_clk_hints_bit = dif_toggle_to_bool(new_state);
268 uint32_t clk_hints_val =
269 mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET);
270 clk_hints_val = bitfield_bit32_write(clk_hints_val, clock, new_clk_hints_bit);
271 mmio_region_write32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET,
272 clk_hints_val);
273
274 return kDifOk;
275#endif
276}
277
278dif_result_t dif_clkmgr_hintable_clock_get_hint(
279 const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
280 dif_toggle_t *state) {
281 if (clkmgr == NULL || state == NULL || !clkmgr_valid_hintable_clock(clock)) {
282 return kDifBadArg;
283 }
284
285#if CLKMGR_PARAM_NUM_HINTABLE_CLOCKS == 0
287#else
288 // NOTE: the code below fails to compile on tops with no hintable clocks,
289 // where CLKMGR_CLK_HINTS_REG_OFFSET is not defined.
290 uint32_t clk_hints_val =
291 mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET);
292 *state = dif_bool_to_toggle(bitfield_bit32_read(clk_hints_val, clock));
293
294 return kDifOk;
295#endif
296}
297
298#if OPENTITAN_CLKMGR_HAS_SW_EXTCLK_REGWEN
299dif_result_t dif_clkmgr_external_clock_control_is_locked(
300 const dif_clkmgr_t *clkmgr, bool *is_locked) {
301 if (clkmgr == NULL || is_locked == NULL) {
302 return kDifBadArg;
303 }
304
305 *is_locked = extclk_control_register_is_locked(clkmgr);
306
307 return kDifOk;
308}
309
310dif_result_t dif_clkmgr_lock_external_clock_control(
311 const dif_clkmgr_t *clkmgr) {
312 if (clkmgr == NULL) {
313 return kDifBadArg;
314 }
315 mmio_region_write32(clkmgr->base_addr, CLKMGR_EXTCLK_CTRL_REGWEN_REG_OFFSET,
316 0);
317 return kDifOk;
318}
319
320dif_result_t dif_clkmgr_external_clock_set_enabled(const dif_clkmgr_t *clkmgr,
321 bool is_low_speed) {
322 uint32_t extclk_ctrl_reg = 0;
323
324 if (clkmgr == NULL) {
325 return kDifBadArg;
326 }
327
328 if (extclk_control_register_is_locked(clkmgr)) {
329 return kDifLocked;
330 }
331
332 extclk_ctrl_reg = bitfield_field32_write(
333 extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_SEL_FIELD, kMultiBitBool4True);
334 extclk_ctrl_reg = bitfield_field32_write(
335 extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_HI_SPEED_SEL_FIELD,
336 is_low_speed ? kMultiBitBool4False : kMultiBitBool4True);
337 mmio_region_write32(clkmgr->base_addr, CLKMGR_EXTCLK_CTRL_REG_OFFSET,
338 extclk_ctrl_reg);
339 return kDifOk;
340}
341
342dif_result_t dif_clkmgr_external_clock_set_disabled(
343 const dif_clkmgr_t *clkmgr) {
344 uint32_t extclk_ctrl_reg = 0;
345
346 if (clkmgr == NULL) {
347 return kDifBadArg;
348 }
349
350 if (extclk_control_register_is_locked(clkmgr)) {
351 return kDifLocked;
352 }
353
354 extclk_ctrl_reg = bitfield_field32_write(
355 extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_SEL_FIELD, kMultiBitBool4False);
356 // This value is irrelevant when the external clock is disabled.
357 extclk_ctrl_reg = bitfield_field32_write(
358 extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_HI_SPEED_SEL_FIELD,
359 kMultiBitBool4True);
360 mmio_region_write32(clkmgr->base_addr, CLKMGR_EXTCLK_CTRL_REG_OFFSET,
361 extclk_ctrl_reg);
362 return kDifOk;
363}
364#endif
365
366dif_result_t dif_clkmgr_measure_ctrl_disable(const dif_clkmgr_t *clkmgr) {
367 if (clkmgr == NULL) {
368 return kDifBadArg;
369 }
370 mmio_region_write32(clkmgr->base_addr, CLKMGR_MEASURE_CTRL_REGWEN_REG_OFFSET,
371 0);
372 return kDifOk;
373}
374
375dif_result_t dif_clkmgr_measure_ctrl_get_enable(const dif_clkmgr_t *clkmgr,
376 dif_toggle_t *state) {
377 if (clkmgr == NULL || state == NULL) {
378 return kDifBadArg;
379 }
380 *state = dif_bool_to_toggle(clkmgr_measure_ctrl_regwen(clkmgr));
381 return kDifOk;
382}
383
384dif_result_t dif_clkmgr_find_measure_clock(const dif_clkmgr_t *clkmgr,
385 dt_clock_t dt_clk,
386 dif_clkmgr_measure_clock_t *clock) {
387 if (clkmgr == NULL || clock == NULL) {
388 return kDifBadArg;
389 }
390 dt_clkmgr_t dt;
391 DIF_RETURN_IF_ERROR(dif_clkmgr_get_dt(clkmgr, &dt));
392 // Query the DT to find the information.
393 for (size_t i = 0; i < dt_clkmgr_measurable_clock_count(dt); i++) {
394 if (dt_clkmgr_measurable_clock(dt, i).clock == dt_clk) {
395 *clock = i;
396 return kDifOk;
397 }
398 }
399 return kDifError;
400}
401
402dif_result_t dif_clkmgr_enable_measure_counts(const dif_clkmgr_t *clkmgr,
403 dif_clkmgr_measure_clock_t clock,
404 uint32_t lo_threshold,
405 uint32_t hi_threshold) {
406 if (clkmgr == NULL) {
407 return kDifBadArg;
408 }
409 if (!clkmgr_measure_ctrl_regwen(clkmgr)) {
410 return kDifLocked;
411 }
412
413 dt_clkmgr_t dt;
414 DIF_RETURN_IF_ERROR(dif_clkmgr_get_dt(clkmgr, &dt));
415 if (clock >= dt_clkmgr_measurable_clock_count(dt)) {
416 return kDifBadArg;
417 }
419
420 uint32_t measure_ctrl_reg = 0;
421 measure_ctrl_reg = bitfield_field32_write(
422 measure_ctrl_reg, info.meas_ctrl_shadowed_lo_field, lo_threshold);
423 measure_ctrl_reg = bitfield_field32_write(
424 measure_ctrl_reg, info.meas_ctrl_shadowed_hi_field, hi_threshold);
425 // Two writes, because these registers are shadowed.
426 mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)info.meas_ctrl_shadowed_off,
427 measure_ctrl_reg);
428 mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)info.meas_ctrl_shadowed_off,
429 measure_ctrl_reg);
430
431 uint32_t measure_en_reg =
432 bitfield_field32_write(0, info.meas_ctrl_en_en_field, kMultiBitBool4True);
433 mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)info.meas_ctrl_en_off,
434 measure_en_reg);
435
436 return kDifOk;
437}
438
439dif_result_t dif_clkmgr_disable_measure_counts(
440 const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock) {
441 if (clkmgr == NULL) {
442 return kDifBadArg;
443 }
444 if (!clkmgr_measure_ctrl_regwen(clkmgr)) {
445 return kDifLocked;
446 }
447
448 dt_clkmgr_t dt;
449 DIF_RETURN_IF_ERROR(dif_clkmgr_get_dt(clkmgr, &dt));
450 if (clock >= dt_clkmgr_measurable_clock_count(dt)) {
451 return kDifBadArg;
452 }
454
455 uint32_t measure_en_reg = bitfield_field32_write(
456 0, info.meas_ctrl_en_en_field, kMultiBitBool4False);
457 mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)info.meas_ctrl_en_off,
458 measure_en_reg);
459 return kDifOk;
460}
461
462dif_result_t dif_clkmgr_measure_counts_get_enable(
463 const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock,
464 dif_toggle_t *state) {
465 if (clkmgr == NULL || state == NULL) {
466 return kDifBadArg;
467 }
468
469 dt_clkmgr_t dt;
470 DIF_RETURN_IF_ERROR(dif_clkmgr_get_dt(clkmgr, &dt));
471 if (clock >= dt_clkmgr_measurable_clock_count(dt)) {
472 return kDifBadArg;
473 }
475
476 uint32_t en_val =
477 mmio_region_read32(clkmgr->base_addr, (ptrdiff_t)info.meas_ctrl_en_off);
478 *state = dif_multi_bit_bool_to_toggle(
479 bitfield_field32_read(en_val, info.meas_ctrl_en_en_field));
480
481 return kDifOk;
482}
483
484dif_result_t dif_clkmgr_measure_counts_get_thresholds(
485 const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock,
486 uint32_t *min_threshold, uint32_t *max_threshold) {
487 if (clkmgr == NULL || min_threshold == NULL || max_threshold == NULL) {
488 return kDifBadArg;
489 }
490
491 dt_clkmgr_t dt;
492 DIF_RETURN_IF_ERROR(dif_clkmgr_get_dt(clkmgr, &dt));
493 if (clock >= dt_clkmgr_measurable_clock_count(dt)) {
494 return kDifBadArg;
495 }
497
498 uint32_t thresholds_val = mmio_region_read32(
499 clkmgr->base_addr, (ptrdiff_t)info.meas_ctrl_shadowed_off);
500 *min_threshold =
501 bitfield_field32_read(thresholds_val, info.meas_ctrl_shadowed_lo_field);
502 *max_threshold =
503 bitfield_field32_read(thresholds_val, info.meas_ctrl_shadowed_hi_field);
504
505 return kDifOk;
506}
507
508dif_result_t dif_clkmgr_recov_err_code_get_codes(
509 const dif_clkmgr_t *clkmgr, dif_clkmgr_recov_err_codes_t *codes) {
510 if (clkmgr == NULL || codes == NULL) {
511 return kDifBadArg;
512 }
513 *codes =
514 mmio_region_read32(clkmgr->base_addr, CLKMGR_RECOV_ERR_CODE_REG_OFFSET);
515 return kDifOk;
516}
517
518dif_result_t dif_clkmgr_recov_err_code_clear_codes(
519 const dif_clkmgr_t *clkmgr, dif_clkmgr_recov_err_codes_t codes) {
520 if (clkmgr == NULL) {
521 return kDifBadArg;
522 }
523 mmio_region_write32(clkmgr->base_addr, CLKMGR_RECOV_ERR_CODE_REG_OFFSET,
524 codes);
525 return kDifOk;
526}
527
528dif_result_t dif_clkmgr_fatal_err_code_get_codes(
529 const dif_clkmgr_t *clkmgr, dif_clkmgr_fatal_err_codes_t *codes) {
530 if (clkmgr == NULL || codes == NULL) {
531 return kDifBadArg;
532 }
533 *codes =
534 mmio_region_read32(clkmgr->base_addr, CLKMGR_FATAL_ERR_CODE_REG_OFFSET);
535 return kDifOk;
536}
537
538#if OPENTITAN_CLKMGR_HAS_SW_EXTCLK_REGWEN
539dif_result_t dif_clkmgr_wait_for_ext_clk_switch(const dif_clkmgr_t *clkmgr) {
540 if (clkmgr == NULL) {
541 return kDifBadArg;
542 }
543 uint32_t ext_status;
544 do {
545 ext_status =
546 mmio_region_read32(clkmgr->base_addr, CLKMGR_EXTCLK_STATUS_REG_OFFSET);
547 } while (ext_status != kMultiBitBool4True);
548 return kDifOk;
549}
550#endif