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 
11 #include "sw/device/lib/base/multibits.h"
13 
14 #include "clkmgr_regs.h" // Generated
15 
16 // TODO: For the moment, CLKMGR_PARAM_NUM_SW_GATEABLE_CLOCKS has to be <= than
17 // 32, as we only support one enable register for gateable clocks.
18 // https://github.com/lowRISC/opentitan/issues/4201
19 static_assert(
20  CLKMGR_PARAM_NUM_SW_GATEABLE_CLOCKS <= CLKMGR_PARAM_REG_WIDTH,
21  "Expected the number of gateable clocks to be <= the width of a CSR.");
22 
23 // TODO: For the moment, CLKMGR_PARAM_NUM_HINTABLE_CLOCKS has to be <= than
24 // 32, as we only support one enable/hint_status register for hintable clocks.
25 // https://github.com/lowRISC/opentitan/issues/4201
26 static_assert(
27  CLKMGR_PARAM_NUM_HINTABLE_CLOCKS <= CLKMGR_PARAM_REG_WIDTH,
28  "Expected the number of hintable clocks to be <= the width of a CSR.");
29 
30 static bool clkmgr_valid_gateable_clock(dif_clkmgr_gateable_clock_t clock) {
31  return clock < CLKMGR_PARAM_NUM_SW_GATEABLE_CLOCKS;
32 }
33 
34 static bool clkmgr_valid_hintable_clock(dif_clkmgr_hintable_clock_t clock) {
35  return clock < CLKMGR_PARAM_NUM_HINTABLE_CLOCKS;
36 }
37 
38 static bool clkmgr_measure_ctrl_regwen(const dif_clkmgr_t *clkmgr) {
39  uint32_t measure_ctrl_regwen_val = mmio_region_read32(
40  clkmgr->base_addr, CLKMGR_MEASURE_CTRL_REGWEN_REG_OFFSET);
41  return bitfield_bit32_read(measure_ctrl_regwen_val,
42  CLKMGR_MEASURE_CTRL_REGWEN_EN_BIT);
43 }
44 
45 /**
46  * Checks if the jitter enable register is locked.
47  *
48  * The jitter enable register is locked by CLKMGR_JITTER_REGWEN.
49  */
51 static bool jitter_enable_register_is_locked(const dif_clkmgr_t *clkmgr) {
52  // Jitter enable register is locked when `CLKMGR_JITTER_REGWEN_EN_BIT` bit
53  // is 0.
54  return !bitfield_bit32_read(
55  mmio_region_read32(clkmgr->base_addr, CLKMGR_JITTER_REGWEN_REG_OFFSET),
56  CLKMGR_JITTER_REGWEN_EN_BIT);
57 }
58 
59 /**
60  * Checks if the external clock control register is locked.
61  *
62  * The external clock control register is locked by CLKMGR_EXTCLK_CTRL_REGWEN.
63  */
65 static bool extclk_control_register_is_locked(const dif_clkmgr_t *clkmgr) {
66  // External clock control register is locked when
67  // `CLKMGR_EXTCLK_CTRL_REGWEN_EN_BIT` bit is 0.
68  return !bitfield_bit32_read(
69  mmio_region_read32(clkmgr->base_addr,
70  CLKMGR_EXTCLK_CTRL_REGWEN_REG_OFFSET),
71  CLKMGR_EXTCLK_CTRL_REGWEN_EN_BIT);
72 }
73 
75  bool *status) {
76  if (clkmgr == NULL || status == NULL) {
77  return kDifBadArg;
78  }
79  uint32_t extclk_status_val =
80  mmio_region_read32(clkmgr->base_addr, CLKMGR_EXTCLK_STATUS_REG_OFFSET);
81  *status = bitfield_field32_read(extclk_status_val,
82  CLKMGR_EXTCLK_STATUS_ACK_FIELD) ==
83  kMultiBitBool4True;
84 
85  return kDifOk;
86 }
87 
89  bool *is_locked) {
90  if (clkmgr == NULL || is_locked == NULL) {
91  return kDifBadArg;
92  }
93 
94  *is_locked = jitter_enable_register_is_locked(clkmgr);
95 
96  return kDifOk;
97 }
98 
99 dif_result_t dif_clkmgr_lock_jitter_enable(const dif_clkmgr_t *clkmgr) {
100  if (clkmgr == NULL) {
101  return kDifBadArg;
102  }
103  mmio_region_write32(clkmgr->base_addr, CLKMGR_JITTER_REGWEN_REG_OFFSET, 0);
104  return kDifOk;
105 }
106 
107 dif_result_t dif_clkmgr_jitter_get_enabled(const dif_clkmgr_t *clkmgr,
108  dif_toggle_t *state) {
109  if (clkmgr == NULL || state == NULL) {
110  return kDifBadArg;
111  }
112 
113  multi_bit_bool_t clk_jitter_val =
114  mmio_region_read32(clkmgr->base_addr, CLKMGR_JITTER_ENABLE_REG_OFFSET);
115  // The documentation states that kMultiBitBool4False disables the jittery
116  // clock and all other values enable the jittery clock.
117  *state = clk_jitter_val != kMultiBitBool4False ? kDifToggleEnabled
119 
120  return kDifOk;
121 }
122 
123 dif_result_t dif_clkmgr_jitter_set_enabled(const dif_clkmgr_t *clkmgr,
124  dif_toggle_t new_state) {
125  multi_bit_bool_t new_jitter_enable_val;
126  if (clkmgr == NULL) {
127  return kDifBadArg;
128  }
129  if (jitter_enable_register_is_locked(clkmgr)) {
130  return kDifLocked;
131  }
132 
133  switch (new_state) {
134  case kDifToggleEnabled:
135  new_jitter_enable_val = kMultiBitBool4True;
136  break;
137  case kDifToggleDisabled:
138  new_jitter_enable_val = kMultiBitBool4False;
139  break;
140  default:
141  return kDifBadArg;
142  }
143  mmio_region_write32(clkmgr->base_addr, CLKMGR_JITTER_ENABLE_REG_OFFSET,
144  new_jitter_enable_val);
145  return kDifOk;
146 }
147 
150  const dif_clkmgr_t *clkmgr, dt_instance_id_t inst_id,
152  if (clkmgr == NULL || clock == NULL) {
153  return kDifBadArg;
154  }
155  dt_clkmgr_t dt;
156  dif_result_t res = dif_clkmgr_get_dt(clkmgr, &dt);
157  if (res != kDifOk) {
158  return res;
159  }
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 
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  uint32_t clk_enables_val =
178  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET);
179  *state = dif_bool_to_toggle(bitfield_bit32_read(clk_enables_val, clock));
180 
181  return kDifOk;
182 }
183 
185  const dif_clkmgr_t *clkmgr, dif_clkmgr_gateable_clock_t clock,
186  dif_toggle_t new_state) {
187  if (clkmgr == NULL || !clkmgr_valid_gateable_clock(clock) ||
188  !dif_is_valid_toggle(new_state)) {
189  return kDifBadArg;
190  }
191 
192  bool new_clk_enables_bit = dif_toggle_to_bool(new_state);
193  uint32_t clk_enables_val =
194  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET);
195  clk_enables_val =
196  bitfield_bit32_write(clk_enables_val, clock, new_clk_enables_bit);
197  mmio_region_write32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET,
198  clk_enables_val);
199 
200  return kDifOk;
201 }
202 
205  const dif_clkmgr_t *clkmgr, dt_instance_id_t inst_id,
207  if (clkmgr == NULL || clock == NULL) {
208  return kDifBadArg;
209  }
210  dt_clkmgr_t dt;
211  dif_result_t res = dif_clkmgr_get_dt(clkmgr, &dt);
212  if (res != kDifOk) {
213  return res;
214  }
215  // Query the DT to find the information.
216  for (size_t i = 0; i < dt_clkmgr_hintable_clock_count(dt); i++) {
217  if (dt_clkmgr_hintable_clock(dt, i) == inst_id) {
218  *clock = i;
219  return kDifOk;
220  }
221  }
222  return kDifError;
223 }
224 
226  const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
227  dif_toggle_t *state) {
228  if (clkmgr == NULL || state == NULL || !clkmgr_valid_hintable_clock(clock)) {
229  return kDifBadArg;
230  }
231 
232  uint32_t clk_hints_val =
233  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_STATUS_REG_OFFSET);
234  *state = dif_bool_to_toggle(bitfield_bit32_read(clk_hints_val, clock));
235 
236  return kDifOk;
237 }
238 
240  const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
241  dif_toggle_t new_state) {
242  if (clkmgr == NULL || !clkmgr_valid_hintable_clock(clock) ||
243  !dif_is_valid_toggle(new_state)) {
244  return kDifBadArg;
245  }
246 
247  bool new_clk_hints_bit = dif_toggle_to_bool(new_state);
248  uint32_t clk_hints_val =
249  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET);
250  clk_hints_val = bitfield_bit32_write(clk_hints_val, clock, new_clk_hints_bit);
251  mmio_region_write32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET,
252  clk_hints_val);
253 
254  return kDifOk;
255 }
256 
258  const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
259  dif_toggle_t *state) {
260  if (clkmgr == NULL || state == NULL || !clkmgr_valid_hintable_clock(clock)) {
261  return kDifBadArg;
262  }
263 
264  uint32_t clk_hints_val =
265  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET);
266  *state = dif_bool_to_toggle(bitfield_bit32_read(clk_hints_val, clock));
267 
268  return kDifOk;
269 }
270 
272  const dif_clkmgr_t *clkmgr, bool *is_locked) {
273  if (clkmgr == NULL || is_locked == NULL) {
274  return kDifBadArg;
275  }
276 
277  *is_locked = extclk_control_register_is_locked(clkmgr);
278 
279  return kDifOk;
280 }
281 
283  const dif_clkmgr_t *clkmgr) {
284  if (clkmgr == NULL) {
285  return kDifBadArg;
286  }
287  mmio_region_write32(clkmgr->base_addr, CLKMGR_EXTCLK_CTRL_REGWEN_REG_OFFSET,
288  0);
289  return kDifOk;
290 }
291 
293  bool is_low_speed) {
294  uint32_t extclk_ctrl_reg = 0;
295 
296  if (clkmgr == NULL) {
297  return kDifBadArg;
298  }
299 
300  if (extclk_control_register_is_locked(clkmgr)) {
301  return kDifLocked;
302  }
303 
304  extclk_ctrl_reg = bitfield_field32_write(
305  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_SEL_FIELD, kMultiBitBool4True);
306  extclk_ctrl_reg = bitfield_field32_write(
307  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_HI_SPEED_SEL_FIELD,
308  is_low_speed ? kMultiBitBool4False : kMultiBitBool4True);
309  mmio_region_write32(clkmgr->base_addr, CLKMGR_EXTCLK_CTRL_REG_OFFSET,
310  extclk_ctrl_reg);
311  return kDifOk;
312 }
313 
315  const dif_clkmgr_t *clkmgr) {
316  uint32_t extclk_ctrl_reg = 0;
317 
318  if (clkmgr == NULL) {
319  return kDifBadArg;
320  }
321 
322  if (extclk_control_register_is_locked(clkmgr)) {
323  return kDifLocked;
324  }
325 
326  extclk_ctrl_reg = bitfield_field32_write(
327  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_SEL_FIELD, kMultiBitBool4False);
328  // This value is irrelevant when the external clock is disabled.
329  extclk_ctrl_reg = bitfield_field32_write(
330  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_HI_SPEED_SEL_FIELD,
331  kMultiBitBool4True);
332  mmio_region_write32(clkmgr->base_addr, CLKMGR_EXTCLK_CTRL_REG_OFFSET,
333  extclk_ctrl_reg);
334  return kDifOk;
335 }
336 
337 dif_result_t dif_clkmgr_measure_ctrl_disable(const dif_clkmgr_t *clkmgr) {
338  if (clkmgr == NULL) {
339  return kDifBadArg;
340  }
341  mmio_region_write32(clkmgr->base_addr, CLKMGR_MEASURE_CTRL_REGWEN_REG_OFFSET,
342  0);
343  return kDifOk;
344 }
345 
347  dif_toggle_t *state) {
348  if (clkmgr == NULL || state == NULL) {
349  return kDifBadArg;
350  }
351  *state = dif_bool_to_toggle(clkmgr_measure_ctrl_regwen(clkmgr));
352  return kDifOk;
353 }
354 
355 // The earlgrey and englishbreakfast CSR differences mean they have a
356 // different number of clock measuremen units. The ideal way to handle
357 // this difference is just generating the dif code, and we intend to do
358 // that with multi-top. However, for the time being we need to rely on
359 // macro trickery, just generating code for a specific measurement if
360 // there is a #define for the corresponding enable CSR offset.
361 //
362 // These macro are a tricky way to implement a macro like the following:
363 // #define CONDITIONALLY_DO \ ...
364 // #ifdef SOMETHING do_something \ ...
365 // #else do_something_else \ ...
366 // #endif
367 // However, such macros are illegal in C. So the macros below implement
368 // the conditional using a suggestion presented in
369 // https://stackoverflow.com/questions/72266480/can-ifdef-be-used-inside-a-macro
370 //
371 // The reason we need these macros is to handle differences between earlgrey
372 // and englishbreakfast without generating the dif code.
373 
374 // TODO(lowrisc/opentitan#19823): Remove this whole macro trickery once this
375 // file is generated.
376 #define CNCAT_IMPL(a_, b_) a_##b_
377 #define CNCAT(a_, b_) CNCAT_IMPL(a_, b_)
378 #define CHECK_CLKMGR_IO_MEAS_CTRL_EN_REG_OFFSET ~, ~
379 #define CHECK_CLKMGR_IO_DIV2_MEAS_CTRL_EN_REG_OFFSET ~, ~
380 #define CHECK_CLKMGR_IO_DIV4_MEAS_CTRL_EN_REG_OFFSET ~, ~
381 #define CHECK_CLKMGR_MAIN_MEAS_CTRL_EN_REG_OFFSET ~, ~
382 #define CHECK_CLKMGR_USB_MEAS_CTRL_EN_REG_OFFSET ~, ~
383 #define CHECK_IMPL(a_, b_, c_, ...) c_
384 #define CHECK(tup_) CHECK_IMPL tup_
385 
386 #define IS_KIND_DEFINED(kind_) \
387  CHECK((CNCAT(CHECK_, CLKMGR_##kind_##_MEAS_CTRL_EN_REG_OFFSET), 0, 1))
388 
389 #define IIF_0(true_exp_, false_exp_) false_exp_
390 #define IIF_1(true_exp_, false_exp_) true_exp_
391 #define IIF(condition_, true_exp_, false_exp_) \
392  CNCAT(IIF_, condition_)(true_exp_, false_exp_)
393 
395  dif_clkmgr_measure_clock_t clock,
396  uint32_t lo_threshold,
397  uint32_t hi_threshold) {
398  if (clkmgr == NULL) {
399  return kDifBadArg;
400  }
401  if (!clkmgr_measure_ctrl_regwen(clkmgr)) {
402  return kDifLocked;
403  }
404 
405  uint32_t en_offset = 0;
406  uint32_t reg_offset = 0;
407  bitfield_field32_t en_field = (bitfield_field32_t){.mask = 0, .index = 0};
408  bitfield_field32_t lo_field = (bitfield_field32_t){.mask = 0, .index = 0};
409  bitfield_field32_t hi_field = (bitfield_field32_t){.mask = 0, .index = 0};
410  switch (clock) {
411 #define PICK_COUNT_CTRL_FIELDS(kind_) \
412  IIF(IS_KIND_DEFINED(kind_), \
413  en_offset = CLKMGR_##kind_##_MEAS_CTRL_EN_REG_OFFSET; \
414  reg_offset = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_REG_OFFSET; \
415  en_field = CLKMGR_##kind_##_MEAS_CTRL_EN_EN_FIELD; \
416  lo_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_LO_FIELD; \
417  hi_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_HI_FIELD; break, break)
418 
419 #if defined(OPENTITAN_IS_EARLGREY)
420  case kDifClkmgrMeasureClockIo:
421  PICK_COUNT_CTRL_FIELDS(IO);
422  case kDifClkmgrMeasureClockIoDiv2:
423  PICK_COUNT_CTRL_FIELDS(IO_DIV2);
424 #elif defined(OPENTITAN_IS_DARJEELING)
425 // Darjeeling does not have Io / IoDiv2 clock measurements
426 #else
427 #error "dif_clkmgr does not support this top"
428 #endif
430  PICK_COUNT_CTRL_FIELDS(IO_DIV4);
432  PICK_COUNT_CTRL_FIELDS(MAIN);
434  PICK_COUNT_CTRL_FIELDS(USB);
435  default:
436  return kDifBadArg;
437 #undef PICK_COUNT_CTRL_FIELDS
438  }
439 
440  uint32_t measure_ctrl_reg = 0;
441  measure_ctrl_reg =
442  bitfield_field32_write(measure_ctrl_reg, lo_field, lo_threshold);
443  measure_ctrl_reg =
444  bitfield_field32_write(measure_ctrl_reg, hi_field, hi_threshold);
445  // Two writes, because these registers are shadowed.
446  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)reg_offset,
447  measure_ctrl_reg);
448  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)reg_offset,
449  measure_ctrl_reg);
450 
451  uint32_t measure_en_reg = 0;
452  measure_en_reg =
453  bitfield_field32_write(measure_en_reg, en_field, kMultiBitBool4True);
454  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)en_offset, measure_en_reg);
455 
456  return kDifOk;
457 }
458 
460  const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock) {
461  if (clkmgr == NULL) {
462  return kDifBadArg;
463  }
464  if (!clkmgr_measure_ctrl_regwen(clkmgr)) {
465  return kDifLocked;
466  }
467 
468  uint32_t en_offset = 0;
469  switch (clock) {
470 #define PICK_EN_OFFSET(kind_) \
471  IIF(IS_KIND_DEFINED(kind_), \
472  en_offset = CLKMGR_##kind_##_MEAS_CTRL_EN_REG_OFFSET; \
473  break, break)
474 
475 #if defined(OPENTITAN_IS_EARLGREY)
476  case kDifClkmgrMeasureClockIo:
477  PICK_EN_OFFSET(IO);
478  break;
479  case kDifClkmgrMeasureClockIoDiv2:
480  PICK_EN_OFFSET(IO_DIV2);
481  break;
482 #elif defined(OPENTITAN_IS_DARJEELING)
483 // Darjeeling does not have Io / IoDiv2 clock measurements
484 #else
485 #error "dif_clkmgr does not support this top"
486 #endif
488  PICK_EN_OFFSET(IO_DIV4);
489  break;
491  PICK_EN_OFFSET(MAIN);
492  break;
494  PICK_EN_OFFSET(USB);
495  break;
496  default:
497  return kDifBadArg;
498 #undef PICK_EN_OFFSET
499  }
500  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)en_offset,
501  kMultiBitBool4False);
502  return kDifOk;
503 }
504 
506  const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock,
507  dif_toggle_t *state) {
508  if (clkmgr == NULL || state == NULL) {
509  return kDifBadArg;
510  }
511 
512  uint32_t en_offset = 0;
513  switch (clock) {
514 #define PICK_EN_OFFSET(kind_) \
515  IIF(IS_KIND_DEFINED(kind_), \
516  en_offset = CLKMGR_##kind_##_MEAS_CTRL_EN_REG_OFFSET; \
517  break, break)
518 
519 #if defined(OPENTITAN_IS_EARLGREY)
520  case kDifClkmgrMeasureClockIo:
521  PICK_EN_OFFSET(IO);
522  case kDifClkmgrMeasureClockIoDiv2:
523  PICK_EN_OFFSET(IO_DIV2);
524 #elif defined(OPENTITAN_IS_DARJEELING)
525 // Darjeeling does not have Io / IoDiv2 clock measurements
526 #else
527 #error "dif_clkmgr does not support this top"
528 #endif
530  PICK_EN_OFFSET(IO_DIV4);
532  PICK_EN_OFFSET(MAIN);
534  PICK_EN_OFFSET(USB);
535  default:
536  return kDifBadArg;
537 #undef PICK_EN_OFFSET
538  }
539  multi_bit_bool_t en_val =
540  mmio_region_read32(clkmgr->base_addr, (ptrdiff_t)en_offset);
541  *state = dif_multi_bit_bool_to_toggle(en_val);
542 
543  return kDifOk;
544 }
545 
547  const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock,
548  uint32_t *min_threshold, uint32_t *max_threshold) {
549  if (clkmgr == NULL || min_threshold == NULL || max_threshold == NULL) {
550  return kDifBadArg;
551  }
552 
553  uint32_t reg_offset = 0;
554  bitfield_field32_t lo_field = (bitfield_field32_t){.mask = 0, .index = 0};
555  bitfield_field32_t hi_field = (bitfield_field32_t){.mask = 0, .index = 0};
556  switch (clock) {
557 #define PICK_THRESHOLD_FIELDS(kind_) \
558  IIF(IS_KIND_DEFINED(kind_), \
559  reg_offset = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_REG_OFFSET; \
560  lo_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_LO_FIELD; \
561  hi_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_HI_FIELD; break, break)
562 #if defined(OPENTITAN_IS_EARLGREY)
563  case kDifClkmgrMeasureClockIo:
564  PICK_THRESHOLD_FIELDS(IO);
565  case kDifClkmgrMeasureClockIoDiv2:
566  PICK_THRESHOLD_FIELDS(IO_DIV2);
567 #elif defined(OPENTITAN_IS_DARJEELING)
568 // Darjeeling does not have Io / IoDiv2 clock measurements
569 #else
570 #error "dif_clkmgr does not support this top"
571 #endif
573  PICK_THRESHOLD_FIELDS(IO_DIV4);
575  PICK_THRESHOLD_FIELDS(MAIN);
577  PICK_THRESHOLD_FIELDS(USB);
578  default:
579  return kDifBadArg;
580 #undef PICK_THRESHOLD_FIELDS
581  }
582  uint32_t thresholds_val =
583  mmio_region_read32(clkmgr->base_addr, (ptrdiff_t)reg_offset);
584  *min_threshold = bitfield_field32_read(thresholds_val, lo_field);
585  *max_threshold = bitfield_field32_read(thresholds_val, hi_field);
586 
587  return kDifOk;
588 }
589 
590 #undef CNCAT_IMPL
591 #undef CNCAT
592 #undef CHECK_CLKMGR_IO_MEAS_CTRL_EN_REG_OFFSET
593 #undef CHECK_CLKMGR_IO_DIV2_MEAS_CTRL_EN_REG_OFFSET
594 #undef CHECK_CLKMGR_IO_DIV4_MEAS_CTRL_EN_REG_OFFSET
595 #undef CHECK_CLKMGR_MAIN_MEAS_CTRL_EN_REG_OFFSET
596 #undef CHECK_CLKMGR_USB_MEAS_CTRL_EN_REG_OFFSET
597 #undef CHECK_IMPL
598 #undef CHECK
599 #undef IS_KIND_DEFINED
600 #undef IIF_0
601 #undef IIF_1
602 #undef IIF
603 
605  const dif_clkmgr_t *clkmgr, dif_clkmgr_recov_err_codes_t *codes) {
606  if (clkmgr == NULL || codes == NULL) {
607  return kDifBadArg;
608  }
609  *codes =
610  mmio_region_read32(clkmgr->base_addr, CLKMGR_RECOV_ERR_CODE_REG_OFFSET);
611  return kDifOk;
612 }
613 
615  const dif_clkmgr_t *clkmgr, dif_clkmgr_recov_err_codes_t codes) {
616  if (clkmgr == NULL) {
617  return kDifBadArg;
618  }
619  mmio_region_write32(clkmgr->base_addr, CLKMGR_RECOV_ERR_CODE_REG_OFFSET,
620  codes);
621  return kDifOk;
622 }
623 
625  const dif_clkmgr_t *clkmgr, dif_clkmgr_fatal_err_codes_t *codes) {
626  if (clkmgr == NULL || codes == NULL) {
627  return kDifBadArg;
628  }
629  *codes =
630  mmio_region_read32(clkmgr->base_addr, CLKMGR_FATAL_ERR_CODE_REG_OFFSET);
631  return kDifOk;
632 }
633 
635  if (clkmgr == NULL) {
636  return kDifBadArg;
637  }
638  uint32_t ext_status;
639  do {
640  ext_status =
641  mmio_region_read32(clkmgr->base_addr, CLKMGR_EXTCLK_STATUS_REG_OFFSET);
642  } while (ext_status != kMultiBitBool4True);
643  return kDifOk;
644 }