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 
46  bool *status) {
47  if (clkmgr == NULL || status == NULL) {
48  return kDifBadArg;
49  }
50  uint32_t extclk_status_val =
51  mmio_region_read32(clkmgr->base_addr, CLKMGR_EXTCLK_STATUS_REG_OFFSET);
52  *status = bitfield_field32_read(extclk_status_val,
53  CLKMGR_EXTCLK_STATUS_ACK_FIELD) ==
54  kMultiBitBool4True;
55 
56  return kDifOk;
57 }
58 
60  dif_toggle_t *state) {
61  if (clkmgr == NULL || state == NULL) {
62  return kDifBadArg;
63  }
64 
65  multi_bit_bool_t clk_jitter_val =
66  mmio_region_read32(clkmgr->base_addr, CLKMGR_JITTER_ENABLE_REG_OFFSET);
67  // The documentation states that kMultiBitBool4False disables the jittery
68  // clock and all other values enable the jittery clock.
69  *state = clk_jitter_val != kMultiBitBool4False;
70 
71  return kDifOk;
72 }
73 
75  dif_toggle_t new_state) {
76  multi_bit_bool_t new_jitter_enable_val;
77  if (clkmgr == NULL) {
78  return kDifBadArg;
79  }
80 
81  switch (new_state) {
82  case kDifToggleEnabled:
83  new_jitter_enable_val = kMultiBitBool4True;
84  break;
85  case kDifToggleDisabled:
86  new_jitter_enable_val = kMultiBitBool4False;
87  break;
88  default:
89  return kDifBadArg;
90  }
91  mmio_region_write32(clkmgr->base_addr, CLKMGR_JITTER_ENABLE_REG_OFFSET,
92  new_jitter_enable_val);
93  return kDifOk;
94 }
95 
97  const dif_clkmgr_t *clkmgr, dif_clkmgr_gateable_clock_t clock,
98  dif_toggle_t *state) {
99  if (clkmgr == NULL || state == NULL || !clkmgr_valid_gateable_clock(clock)) {
100  return kDifBadArg;
101  }
102 
103  uint32_t clk_enables_val =
104  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET);
105  *state = dif_bool_to_toggle(bitfield_bit32_read(clk_enables_val, clock));
106 
107  return kDifOk;
108 }
109 
111  const dif_clkmgr_t *clkmgr, dif_clkmgr_gateable_clock_t clock,
112  dif_toggle_t new_state) {
113  if (clkmgr == NULL || !clkmgr_valid_gateable_clock(clock) ||
114  !dif_is_valid_toggle(new_state)) {
115  return kDifBadArg;
116  }
117 
118  bool new_clk_enables_bit = dif_toggle_to_bool(new_state);
119  uint32_t clk_enables_val =
120  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET);
121  clk_enables_val =
122  bitfield_bit32_write(clk_enables_val, clock, new_clk_enables_bit);
123  mmio_region_write32(clkmgr->base_addr, CLKMGR_CLK_ENABLES_REG_OFFSET,
124  clk_enables_val);
125 
126  return kDifOk;
127 }
128 
130  const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
131  dif_toggle_t *state) {
132  if (clkmgr == NULL || state == NULL || !clkmgr_valid_hintable_clock(clock)) {
133  return kDifBadArg;
134  }
135 
136  uint32_t clk_hints_val =
137  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_STATUS_REG_OFFSET);
138  *state = dif_bool_to_toggle(bitfield_bit32_read(clk_hints_val, clock));
139 
140  return kDifOk;
141 }
142 
144  const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
145  dif_toggle_t new_state) {
146  if (clkmgr == NULL || !clkmgr_valid_hintable_clock(clock) ||
147  !dif_is_valid_toggle(new_state)) {
148  return kDifBadArg;
149  }
150 
151  bool new_clk_hints_bit = dif_toggle_to_bool(new_state);
152  uint32_t clk_hints_val =
153  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET);
154  clk_hints_val = bitfield_bit32_write(clk_hints_val, clock, new_clk_hints_bit);
155  mmio_region_write32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET,
156  clk_hints_val);
157 
158  return kDifOk;
159 }
160 
162  const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
163  dif_toggle_t *state) {
164  if (clkmgr == NULL || state == NULL || !clkmgr_valid_hintable_clock(clock)) {
165  return kDifBadArg;
166  }
167 
168  uint32_t clk_hints_val =
169  mmio_region_read32(clkmgr->base_addr, CLKMGR_CLK_HINTS_REG_OFFSET);
170  *state = dif_bool_to_toggle(bitfield_bit32_read(clk_hints_val, clock));
171 
172  return kDifOk;
173 }
174 
176  bool is_low_speed) {
177  uint32_t extclk_ctrl_reg = 0;
178 
179  if (clkmgr == NULL) {
180  return kDifBadArg;
181  }
182 
183  extclk_ctrl_reg = bitfield_field32_write(
184  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_SEL_FIELD, kMultiBitBool4True);
185  extclk_ctrl_reg = bitfield_field32_write(
186  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_HI_SPEED_SEL_FIELD,
187  is_low_speed ? kMultiBitBool4False : kMultiBitBool4True);
188  mmio_region_write32(clkmgr->base_addr, CLKMGR_EXTCLK_CTRL_REG_OFFSET,
189  extclk_ctrl_reg);
190  return kDifOk;
191 }
192 
194  const dif_clkmgr_t *clkmgr) {
195  uint32_t extclk_ctrl_reg = 0;
196 
197  if (clkmgr == NULL) {
198  return kDifBadArg;
199  }
200 
201  extclk_ctrl_reg = bitfield_field32_write(
202  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_SEL_FIELD, kMultiBitBool4False);
203  // This value is irrelevant when the external clock is disabled.
204  extclk_ctrl_reg = bitfield_field32_write(
205  extclk_ctrl_reg, CLKMGR_EXTCLK_CTRL_HI_SPEED_SEL_FIELD,
206  kMultiBitBool4True);
207  mmio_region_write32(clkmgr->base_addr, CLKMGR_EXTCLK_CTRL_REG_OFFSET,
208  extclk_ctrl_reg);
209  return kDifOk;
210 }
211 
213  if (clkmgr == NULL) {
214  return kDifBadArg;
215  }
216  mmio_region_write32(clkmgr->base_addr, CLKMGR_MEASURE_CTRL_REGWEN_REG_OFFSET,
217  0);
218  return kDifOk;
219 }
220 
222  dif_toggle_t *state) {
223  if (clkmgr == NULL || state == NULL) {
224  return kDifBadArg;
225  }
226  *state = dif_bool_to_toggle(clkmgr_measure_ctrl_regwen(clkmgr));
227  return kDifOk;
228 }
229 
230 // The earlgrey and englishbreakfast CSR differences mean they have a
231 // different number of clock measuremen units. The ideal way to handle
232 // this difference is just generating the dif code, and we intend to do
233 // that with multi-top. However, for the time being we need to rely on
234 // macro trickery, just generating code for a specific measurement if
235 // there is a #define for the corresponding enable CSR offset.
236 //
237 // These macro are a tricky way to implement a macro like the following:
238 // #define CONDITIONALLY_DO \
239 // #ifdef SOMETHING do_something \
240 // #else do_something_else \
241 // #endif
242 // However, such macros are illegal in C. So the macros below implement
243 // the conditional using a suggestion presented in
244 // https://stackoverflow.com/questions/72266480/can-ifdef-be-used-inside-a-macro
245 //
246 // The reason we need these macros is to handle differences between earlgrey
247 // and englishbreakfast without generating the dif code.
248 
249 // TODO(lowrisc/opentitan#19823): Remove this whole macro trickery once this
250 // file is generated.
251 #define CNCAT_IMPL(a_, b_) a_##b_
252 #define CNCAT(a_, b_) CNCAT_IMPL(a_, b_)
253 #define CHECK_CLKMGR_IO_MEAS_CTRL_EN_REG_OFFSET ~, ~
254 #define CHECK_CLKMGR_IO_DIV2_MEAS_CTRL_EN_REG_OFFSET ~, ~
255 #define CHECK_CLKMGR_IO_DIV4_MEAS_CTRL_EN_REG_OFFSET ~, ~
256 #define CHECK_CLKMGR_MAIN_MEAS_CTRL_EN_REG_OFFSET ~, ~
257 #define CHECK_CLKMGR_USB_MEAS_CTRL_EN_REG_OFFSET ~, ~
258 #define CHECK_IMPL(a_, b_, c_, ...) c_
259 #define CHECK(tup_) CHECK_IMPL tup_
260 
261 #define IS_KIND_DEFINED(kind_) \
262  CHECK((CNCAT(CHECK_, CLKMGR_##kind_##_MEAS_CTRL_EN_REG_OFFSET), 0, 1))
263 
264 #define IIF_0(true_exp_, false_exp_) false_exp_
265 #define IIF_1(true_exp_, false_exp_) true_exp_
266 #define IIF(condition_, true_exp_, false_exp_) \
267  CNCAT(IIF_, condition_)(true_exp_, false_exp_)
268 
270  dif_clkmgr_measure_clock_t clock,
271  uint32_t lo_threshold,
272  uint32_t hi_threshold) {
273  if (clkmgr == NULL) {
274  return kDifBadArg;
275  }
276  if (!clkmgr_measure_ctrl_regwen(clkmgr)) {
277  return kDifLocked;
278  }
279 
280  uint32_t en_offset = 0;
281  uint32_t reg_offset = 0;
282  bitfield_field32_t en_field = (bitfield_field32_t){.mask = 0, .index = 0};
283  bitfield_field32_t lo_field = (bitfield_field32_t){.mask = 0, .index = 0};
284  bitfield_field32_t hi_field = (bitfield_field32_t){.mask = 0, .index = 0};
285  switch (clock) {
286 #define PICK_COUNT_CTRL_FIELDS(kind_) \
287  IIF(IS_KIND_DEFINED(kind_), \
288  en_offset = CLKMGR_##kind_##_MEAS_CTRL_EN_REG_OFFSET; \
289  reg_offset = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_REG_OFFSET; \
290  en_field = CLKMGR_##kind_##_MEAS_CTRL_EN_EN_FIELD; \
291  lo_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_LO_FIELD; \
292  hi_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_HI_FIELD; break, break)
293 
295  PICK_COUNT_CTRL_FIELDS(IO);
297  PICK_COUNT_CTRL_FIELDS(IO_DIV2);
299  PICK_COUNT_CTRL_FIELDS(IO_DIV4);
301  PICK_COUNT_CTRL_FIELDS(MAIN);
303  PICK_COUNT_CTRL_FIELDS(USB);
304  default:
305  return kDifBadArg;
306 #undef PICK_COUNT_CTRL_FIELDS
307  }
308 
309  uint32_t measure_ctrl_reg = 0;
310  measure_ctrl_reg =
311  bitfield_field32_write(measure_ctrl_reg, lo_field, lo_threshold);
312  measure_ctrl_reg =
313  bitfield_field32_write(measure_ctrl_reg, hi_field, hi_threshold);
314  // Two writes, because these registers are shadowed.
315  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)reg_offset,
316  measure_ctrl_reg);
317  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)reg_offset,
318  measure_ctrl_reg);
319 
320  uint32_t measure_en_reg = 0;
321  measure_en_reg =
322  bitfield_field32_write(measure_en_reg, en_field, kMultiBitBool4True);
323  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)en_offset, measure_en_reg);
324 
325  return kDifOk;
326 }
327 
329  const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock) {
330  if (clkmgr == NULL) {
331  return kDifBadArg;
332  }
333  if (!clkmgr_measure_ctrl_regwen(clkmgr)) {
334  return kDifLocked;
335  }
336 
337  uint32_t en_offset = 0;
338  switch (clock) {
339 #define PICK_EN_OFFSET(kind_) \
340  IIF(IS_KIND_DEFINED(kind_), \
341  en_offset = CLKMGR_##kind_##_MEAS_CTRL_EN_REG_OFFSET; \
342  break, break)
343 
345  PICK_EN_OFFSET(IO);
346  break;
348  PICK_EN_OFFSET(IO_DIV2);
349  break;
351  PICK_EN_OFFSET(IO_DIV4);
352  break;
354  PICK_EN_OFFSET(MAIN);
355  break;
357  PICK_EN_OFFSET(USB);
358  break;
359  default:
360  return kDifBadArg;
361 #undef PICK_EN_OFFSET
362  }
363  mmio_region_write32(clkmgr->base_addr, (ptrdiff_t)en_offset,
364  kMultiBitBool4False);
365  return kDifOk;
366 }
367 
369  const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock,
370  dif_toggle_t *state) {
371  if (clkmgr == NULL || state == NULL) {
372  return kDifBadArg;
373  }
374 
375  uint32_t en_offset = 0;
376  switch (clock) {
377 #define PICK_EN_OFFSET(kind_) \
378  IIF(IS_KIND_DEFINED(kind_), \
379  en_offset = CLKMGR_##kind_##_MEAS_CTRL_EN_REG_OFFSET; \
380  break, break)
381 
383  PICK_EN_OFFSET(IO);
385  PICK_EN_OFFSET(IO_DIV2);
387  PICK_EN_OFFSET(IO_DIV4);
389  PICK_EN_OFFSET(MAIN);
391  PICK_EN_OFFSET(USB);
392  default:
393  return kDifBadArg;
394 #undef PICK_EN_OFFSET
395  }
396  multi_bit_bool_t en_val =
397  mmio_region_read32(clkmgr->base_addr, (ptrdiff_t)en_offset);
398  *state = dif_multi_bit_bool_to_toggle(en_val);
399 
400  return kDifOk;
401 }
402 
404  const dif_clkmgr_t *clkmgr, dif_clkmgr_measure_clock_t clock,
405  uint32_t *min_threshold, uint32_t *max_threshold) {
406  if (clkmgr == NULL || min_threshold == NULL || max_threshold == NULL) {
407  return kDifBadArg;
408  }
409 
410  uint32_t reg_offset = 0;
411  bitfield_field32_t lo_field = (bitfield_field32_t){.mask = 0, .index = 0};
412  bitfield_field32_t hi_field = (bitfield_field32_t){.mask = 0, .index = 0};
413  switch (clock) {
414 #define PICK_THRESHOLD_FIELDS(kind_) \
415  IIF(IS_KIND_DEFINED(kind_), \
416  reg_offset = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_REG_OFFSET; \
417  lo_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_LO_FIELD; \
418  hi_field = CLKMGR_##kind_##_MEAS_CTRL_SHADOWED_HI_FIELD; break, break)
420  PICK_THRESHOLD_FIELDS(IO);
422  PICK_THRESHOLD_FIELDS(IO_DIV2);
424  PICK_THRESHOLD_FIELDS(IO_DIV4);
426  PICK_THRESHOLD_FIELDS(MAIN);
428  PICK_THRESHOLD_FIELDS(USB);
429  default:
430  return kDifBadArg;
431 #undef PICK_THRESHOLD_FIELDS
432  }
433  uint32_t thresholds_val =
434  mmio_region_read32(clkmgr->base_addr, (ptrdiff_t)reg_offset);
435  *min_threshold = bitfield_field32_read(thresholds_val, lo_field);
436  *max_threshold = bitfield_field32_read(thresholds_val, hi_field);
437 
438  return kDifOk;
439 }
440 
441 #undef CNCAT_IMPL
442 #undef CNCAT
443 #undef CHECK_CLKMGR_IO_MEAS_CTRL_EN_REG_OFFSET
444 #undef CHECK_CLKMGR_IO_DIV2_MEAS_CTRL_EN_REG_OFFSET
445 #undef CHECK_CLKMGR_IO_DIV4_MEAS_CTRL_EN_REG_OFFSET
446 #undef CHECK_CLKMGR_MAIN_MEAS_CTRL_EN_REG_OFFSET
447 #undef CHECK_CLKMGR_USB_MEAS_CTRL_EN_REG_OFFSET
448 #undef CHECK_IMPL
449 #undef CHECK
450 #undef IS_KIND_DEFINED
451 #undef IIF_0
452 #undef IIF_1
453 #undef IIF
454 
456  const dif_clkmgr_t *clkmgr, dif_clkmgr_recov_err_codes_t *codes) {
457  if (clkmgr == NULL || codes == NULL) {
458  return kDifBadArg;
459  }
460  *codes =
461  mmio_region_read32(clkmgr->base_addr, CLKMGR_RECOV_ERR_CODE_REG_OFFSET);
462  return kDifOk;
463 }
464 
466  const dif_clkmgr_t *clkmgr, dif_clkmgr_recov_err_codes_t codes) {
467  if (clkmgr == NULL) {
468  return kDifBadArg;
469  }
470  mmio_region_write32(clkmgr->base_addr, CLKMGR_RECOV_ERR_CODE_REG_OFFSET,
471  codes);
472  return kDifOk;
473 }
474 
476  const dif_clkmgr_t *clkmgr, dif_clkmgr_fatal_err_codes_t *codes) {
477  if (clkmgr == NULL || codes == NULL) {
478  return kDifBadArg;
479  }
480  *codes =
481  mmio_region_read32(clkmgr->base_addr, CLKMGR_FATAL_ERR_CODE_REG_OFFSET);
482  return kDifOk;
483 }
484 
486  if (clkmgr == NULL) {
487  return kDifBadArg;
488  }
489  uint32_t ext_status;
490  do {
491  ext_status =
492  mmio_region_read32(clkmgr->base_addr, CLKMGR_EXTCLK_STATUS_REG_OFFSET);
493  } while (ext_status != kMultiBitBool4True);
494  return kDifOk;
495 }