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 
149  const dif_clkmgr_t *clkmgr, dif_clkmgr_gateable_clock_t clock,
150  dif_toggle_t *state) {
151  if (clkmgr == NULL || state == NULL || !clkmgr_valid_gateable_clock(clock)) {
152  return kDifBadArg;
153  }
154 
155  uint32_t clk_enables_val =
156  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET);
157  *state = dif_bool_to_toggle(bitfield_bit32_read(clk_enables_val, clock));
158 
159  return kDifOk;
160 }
161 
163  const dif_clkmgr_t *clkmgr, dif_clkmgr_gateable_clock_t clock,
164  dif_toggle_t new_state) {
165  if (clkmgr == NULL || !clkmgr_valid_gateable_clock(clock) ||
166  !dif_is_valid_toggle(new_state)) {
167  return kDifBadArg;
168  }
169 
170  bool new_clk_enables_bit = dif_toggle_to_bool(new_state);
171  uint32_t clk_enables_val =
172  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET);
173  clk_enables_val =
174  bitfield_bit32_write(clk_enables_val, clock, new_clk_enables_bit);
175  mmio_region_write32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET,
176  clk_enables_val);
177 
178  return kDifOk;
179 }
180 
182  const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
183  dif_toggle_t *state) {
184  if (clkmgr == NULL || state == NULL || !clkmgr_valid_hintable_clock(clock)) {
185  return kDifBadArg;
186  }
187 
188  uint32_t clk_hints_val =
189  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_STATUS_REG_OFFSET);
190  *state = dif_bool_to_toggle(bitfield_bit32_read(clk_hints_val, clock));
191 
192  return kDifOk;
193 }
194 
196  const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
197  dif_toggle_t new_state) {
198  if (clkmgr == NULL || !clkmgr_valid_hintable_clock(clock) ||
199  !dif_is_valid_toggle(new_state)) {
200  return kDifBadArg;
201  }
202 
203  bool new_clk_hints_bit = dif_toggle_to_bool(new_state);
204  uint32_t clk_hints_val =
205  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET);
206  clk_hints_val = bitfield_bit32_write(clk_hints_val, clock, new_clk_hints_bit);
207  mmio_region_write32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET,
208  clk_hints_val);
209 
210  return kDifOk;
211 }
212 
214  const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
215  dif_toggle_t *state) {
216  if (clkmgr == NULL || state == NULL || !clkmgr_valid_hintable_clock(clock)) {
217  return kDifBadArg;
218  }
219 
220  uint32_t clk_hints_val =
221  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET);
222  *state = dif_bool_to_toggle(bitfield_bit32_read(clk_hints_val, clock));
223 
224  return kDifOk;
225 }
226 
228  const dif_clkmgr_t *clkmgr, bool *is_locked) {
229  if (clkmgr == NULL || is_locked == NULL) {
230  return kDifBadArg;
231  }
232 
233  *is_locked = extclk_control_register_is_locked(clkmgr);
234 
235  return kDifOk;
236 }
237 
239  const dif_clkmgr_t *clkmgr) {
240  if (clkmgr == NULL) {
241  return kDifBadArg;
242  }
243  mmio_region_write32(clkmgr->base_addr, CLKMGR_EXTCLK_CTRL_REGWEN_REG_OFFSET,
244  0);
245  return kDifOk;
246 }
247 
249  bool is_low_speed) {
250  uint32_t extclk_ctrl_reg = 0;
251 
252  if (clkmgr == NULL) {
253  return kDifBadArg;
254  }
255 
256  if (extclk_control_register_is_locked(clkmgr)) {
257  return kDifLocked;
258  }
259 
260  extclk_ctrl_reg = bitfield_field32_write(
261  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_SEL_FIELD, kMultiBitBool4True);
262  extclk_ctrl_reg = bitfield_field32_write(
263  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_HI_SPEED_SEL_FIELD,
264  is_low_speed ? kMultiBitBool4False : kMultiBitBool4True);
265  mmio_region_write32(clkmgr->base_addr, CLKMGR_EXTCLK_CTRL_REG_OFFSET,
266  extclk_ctrl_reg);
267  return kDifOk;
268 }
269 
271  const dif_clkmgr_t *clkmgr) {
272  uint32_t extclk_ctrl_reg = 0;
273 
274  if (clkmgr == NULL) {
275  return kDifBadArg;
276  }
277 
278  if (extclk_control_register_is_locked(clkmgr)) {
279  return kDifLocked;
280  }
281 
282  extclk_ctrl_reg = bitfield_field32_write(
283  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_SEL_FIELD, kMultiBitBool4False);
284  // This value is irrelevant when the external clock is disabled.
285  extclk_ctrl_reg = bitfield_field32_write(
286  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_HI_SPEED_SEL_FIELD,
287  kMultiBitBool4True);
288  mmio_region_write32(clkmgr->base_addr, CLKMGR_EXTCLK_CTRL_REG_OFFSET,
289  extclk_ctrl_reg);
290  return kDifOk;
291 }
292 
293 dif_result_t dif_clkmgr_measure_ctrl_disable(const dif_clkmgr_t *clkmgr) {
294  if (clkmgr == NULL) {
295  return kDifBadArg;
296  }
297  mmio_region_write32(clkmgr->base_addr, CLKMGR_MEASURE_CTRL_REGWEN_REG_OFFSET,
298  0);
299  return kDifOk;
300 }
301 
303  dif_toggle_t *state) {
304  if (clkmgr == NULL || state == NULL) {
305  return kDifBadArg;
306  }
307  *state = dif_bool_to_toggle(clkmgr_measure_ctrl_regwen(clkmgr));
308  return kDifOk;
309 }
310 
311 // The earlgrey and englishbreakfast CSR differences mean they have a
312 // different number of clock measuremen units. The ideal way to handle
313 // this difference is just generating the dif code, and we intend to do
314 // that with multi-top. However, for the time being we need to rely on
315 // macro trickery, just generating code for a specific measurement if
316 // there is a #define for the corresponding enable CSR offset.
317 //
318 // These macro are a tricky way to implement a macro like the following:
319 // #define CONDITIONALLY_DO \ ...
320 // #ifdef SOMETHING do_something \ ...
321 // #else do_something_else \ ...
322 // #endif
323 // However, such macros are illegal in C. So the macros below implement
324 // the conditional using a suggestion presented in
325 // https://stackoverflow.com/questions/72266480/can-ifdef-be-used-inside-a-macro
326 //
327 // The reason we need these macros is to handle differences between earlgrey
328 // and englishbreakfast without generating the dif code.
329 
330 // TODO(lowrisc/opentitan#19823): Remove this whole macro trickery once this
331 // file is generated.
332 #define CNCAT_IMPL(a_, b_) a_##b_
333 #define CNCAT(a_, b_) CNCAT_IMPL(a_, b_)
334 #define CHECK_CLKMGR_IO_MEAS_CTRL_EN_REG_OFFSET ~, ~
335 #define CHECK_CLKMGR_IO_DIV2_MEAS_CTRL_EN_REG_OFFSET ~, ~
336 #define CHECK_CLKMGR_IO_DIV4_MEAS_CTRL_EN_REG_OFFSET ~, ~
337 #define CHECK_CLKMGR_MAIN_MEAS_CTRL_EN_REG_OFFSET ~, ~
338 #define CHECK_CLKMGR_USB_MEAS_CTRL_EN_REG_OFFSET ~, ~
339 #define CHECK_IMPL(a_, b_, c_, ...) c_
340 #define CHECK(tup_) CHECK_IMPL tup_
341 
342 #define IS_KIND_DEFINED(kind_) \
343  CHECK((CNCAT(CHECK_, CLKMGR_##kind_##_MEAS_CTRL_EN_REG_OFFSET), 0, 1))
344 
345 #define IIF_0(true_exp_, false_exp_) false_exp_
346 #define IIF_1(true_exp_, false_exp_) true_exp_
347 #define IIF(condition_, true_exp_, false_exp_) \
348  CNCAT(IIF_, condition_)(true_exp_, false_exp_)
349 
351  dif_clkmgr_measure_clock_t clock,
352  uint32_t lo_threshold,
353  uint32_t hi_threshold) {
354  if (clkmgr == NULL) {
355  return kDifBadArg;
356  }
357  if (!clkmgr_measure_ctrl_regwen(clkmgr)) {
358  return kDifLocked;
359  }
360 
361  uint32_t en_offset = 0;
362  uint32_t reg_offset = 0;
363  bitfield_field32_t en_field = (bitfield_field32_t){.mask = 0, .index = 0};
364  bitfield_field32_t lo_field = (bitfield_field32_t){.mask = 0, .index = 0};
365  bitfield_field32_t hi_field = (bitfield_field32_t){.mask = 0, .index = 0};
366  switch (clock) {
367 #define PICK_COUNT_CTRL_FIELDS(kind_) \
368  IIF(IS_KIND_DEFINED(kind_), \
369  en_offset = CLKMGR_##kind_##_MEAS_CTRL_EN_REG_OFFSET; \
370  reg_offset = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_REG_OFFSET; \
371  en_field = CLKMGR_##kind_##_MEAS_CTRL_EN_EN_FIELD; \
372  lo_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_LO_FIELD; \
373  hi_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_HI_FIELD; break, break)
374 
375 #if defined(OPENTITAN_IS_EARLGREY)
376  case kDifClkmgrMeasureClockIo:
377  PICK_COUNT_CTRL_FIELDS(IO);
378  case kDifClkmgrMeasureClockIoDiv2:
379  PICK_COUNT_CTRL_FIELDS(IO_DIV2);
380 #elif defined(OPENTITAN_IS_DARJEELING)
381 // Darjeeling does not have Io / IoDiv2 clock measurements
382 #else
383 #error "dif_clkmgr does not support this top"
384 #endif
386  PICK_COUNT_CTRL_FIELDS(IO_DIV4);
388  PICK_COUNT_CTRL_FIELDS(MAIN);
390  PICK_COUNT_CTRL_FIELDS(USB);
391  default:
392  return kDifBadArg;
393 #undef PICK_COUNT_CTRL_FIELDS
394  }
395 
396  uint32_t measure_ctrl_reg = 0;
397  measure_ctrl_reg =
398  bitfield_field32_write(measure_ctrl_reg, lo_field, lo_threshold);
399  measure_ctrl_reg =
400  bitfield_field32_write(measure_ctrl_reg, hi_field, hi_threshold);
401  // Two writes, because these registers are shadowed.
402  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)reg_offset,
403  measure_ctrl_reg);
404  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)reg_offset,
405  measure_ctrl_reg);
406 
407  uint32_t measure_en_reg = 0;
408  measure_en_reg =
409  bitfield_field32_write(measure_en_reg, en_field, kMultiBitBool4True);
410  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)en_offset, measure_en_reg);
411 
412  return kDifOk;
413 }
414 
416  const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock) {
417  if (clkmgr == NULL) {
418  return kDifBadArg;
419  }
420  if (!clkmgr_measure_ctrl_regwen(clkmgr)) {
421  return kDifLocked;
422  }
423 
424  uint32_t en_offset = 0;
425  switch (clock) {
426 #define PICK_EN_OFFSET(kind_) \
427  IIF(IS_KIND_DEFINED(kind_), \
428  en_offset = CLKMGR_##kind_##_MEAS_CTRL_EN_REG_OFFSET; \
429  break, break)
430 
431 #if defined(OPENTITAN_IS_EARLGREY)
432  case kDifClkmgrMeasureClockIo:
433  PICK_EN_OFFSET(IO);
434  break;
435  case kDifClkmgrMeasureClockIoDiv2:
436  PICK_EN_OFFSET(IO_DIV2);
437  break;
438 #elif defined(OPENTITAN_IS_DARJEELING)
439 // Darjeeling does not have Io / IoDiv2 clock measurements
440 #else
441 #error "dif_clkmgr does not support this top"
442 #endif
444  PICK_EN_OFFSET(IO_DIV4);
445  break;
447  PICK_EN_OFFSET(MAIN);
448  break;
450  PICK_EN_OFFSET(USB);
451  break;
452  default:
453  return kDifBadArg;
454 #undef PICK_EN_OFFSET
455  }
456  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)en_offset,
457  kMultiBitBool4False);
458  return kDifOk;
459 }
460 
462  const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock,
463  dif_toggle_t *state) {
464  if (clkmgr == NULL || state == NULL) {
465  return kDifBadArg;
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  case kDifClkmgrMeasureClockIoDiv2:
479  PICK_EN_OFFSET(IO_DIV2);
480 #elif defined(OPENTITAN_IS_DARJEELING)
481 // Darjeeling does not have Io / IoDiv2 clock measurements
482 #else
483 #error "dif_clkmgr does not support this top"
484 #endif
486  PICK_EN_OFFSET(IO_DIV4);
488  PICK_EN_OFFSET(MAIN);
490  PICK_EN_OFFSET(USB);
491  default:
492  return kDifBadArg;
493 #undef PICK_EN_OFFSET
494  }
495  multi_bit_bool_t en_val =
496  mmio_region_read32(clkmgr->base_addr, (ptrdiff_t)en_offset);
497  *state = dif_multi_bit_bool_to_toggle(en_val);
498 
499  return kDifOk;
500 }
501 
503  const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock,
504  uint32_t *min_threshold, uint32_t *max_threshold) {
505  if (clkmgr == NULL || min_threshold == NULL || max_threshold == NULL) {
506  return kDifBadArg;
507  }
508 
509  uint32_t reg_offset = 0;
510  bitfield_field32_t lo_field = (bitfield_field32_t){.mask = 0, .index = 0};
511  bitfield_field32_t hi_field = (bitfield_field32_t){.mask = 0, .index = 0};
512  switch (clock) {
513 #define PICK_THRESHOLD_FIELDS(kind_) \
514  IIF(IS_KIND_DEFINED(kind_), \
515  reg_offset = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_REG_OFFSET; \
516  lo_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_LO_FIELD; \
517  hi_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_HI_FIELD; break, break)
518 #if defined(OPENTITAN_IS_EARLGREY)
519  case kDifClkmgrMeasureClockIo:
520  PICK_THRESHOLD_FIELDS(IO);
521  case kDifClkmgrMeasureClockIoDiv2:
522  PICK_THRESHOLD_FIELDS(IO_DIV2);
523 #elif defined(OPENTITAN_IS_DARJEELING)
524 // Darjeeling does not have Io / IoDiv2 clock measurements
525 #else
526 #error "dif_clkmgr does not support this top"
527 #endif
529  PICK_THRESHOLD_FIELDS(IO_DIV4);
531  PICK_THRESHOLD_FIELDS(MAIN);
533  PICK_THRESHOLD_FIELDS(USB);
534  default:
535  return kDifBadArg;
536 #undef PICK_THRESHOLD_FIELDS
537  }
538  uint32_t thresholds_val =
539  mmio_region_read32(clkmgr->base_addr, (ptrdiff_t)reg_offset);
540  *min_threshold = bitfield_field32_read(thresholds_val, lo_field);
541  *max_threshold = bitfield_field32_read(thresholds_val, hi_field);
542 
543  return kDifOk;
544 }
545 
546 #undef CNCAT_IMPL
547 #undef CNCAT
548 #undef CHECK_CLKMGR_IO_MEAS_CTRL_EN_REG_OFFSET
549 #undef CHECK_CLKMGR_IO_DIV2_MEAS_CTRL_EN_REG_OFFSET
550 #undef CHECK_CLKMGR_IO_DIV4_MEAS_CTRL_EN_REG_OFFSET
551 #undef CHECK_CLKMGR_MAIN_MEAS_CTRL_EN_REG_OFFSET
552 #undef CHECK_CLKMGR_USB_MEAS_CTRL_EN_REG_OFFSET
553 #undef CHECK_IMPL
554 #undef CHECK
555 #undef IS_KIND_DEFINED
556 #undef IIF_0
557 #undef IIF_1
558 #undef IIF
559 
561  const dif_clkmgr_t *clkmgr, dif_clkmgr_recov_err_codes_t *codes) {
562  if (clkmgr == NULL || codes == NULL) {
563  return kDifBadArg;
564  }
565  *codes =
566  mmio_region_read32(clkmgr->base_addr, CLKMGR_RECOV_ERR_CODE_REG_OFFSET);
567  return kDifOk;
568 }
569 
571  const dif_clkmgr_t *clkmgr, dif_clkmgr_recov_err_codes_t codes) {
572  if (clkmgr == NULL) {
573  return kDifBadArg;
574  }
575  mmio_region_write32(clkmgr->base_addr, CLKMGR_RECOV_ERR_CODE_REG_OFFSET,
576  codes);
577  return kDifOk;
578 }
579 
581  const dif_clkmgr_t *clkmgr, dif_clkmgr_fatal_err_codes_t *codes) {
582  if (clkmgr == NULL || codes == NULL) {
583  return kDifBadArg;
584  }
585  *codes =
586  mmio_region_read32(clkmgr->base_addr, CLKMGR_FATAL_ERR_CODE_REG_OFFSET);
587  return kDifOk;
588 }
589 
591  if (clkmgr == NULL) {
592  return kDifBadArg;
593  }
594  uint32_t ext_status;
595  do {
596  ext_status =
597  mmio_region_read32(clkmgr->base_addr, CLKMGR_EXTCLK_STATUS_REG_OFFSET);
598  } while (ext_status != kMultiBitBool4True);
599  return kDifOk;
600 }