Software APIs
dif_pwrmgr.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 
13 #include "pwrmgr_regs.h" // Generated.
14 
15 /**
16  * Following static assertions make sure that generated values match the
17  * definitions in the header, which we rely on for a simpler implementation.
18  * These constants and their usages must be revisited if there is a change in
19  * hardware.
20  */
21 
22 /**
23  * Relevant bits of the control register must start at
24  * `PWRMGR_CONTROL_CORE_CLK_EN_BIT` and be in the same order as
25  * `dif_pwrmgr_domain_option_t` constants.
26  */
28  (1u << (PWRMGR_CONTROL_CORE_CLK_EN_BIT -
29  PWRMGR_CONTROL_CORE_CLK_EN_BIT)),
30  "Layout of control register changed.");
31 
33  (1u << (PWRMGR_CONTROL_IO_CLK_EN_BIT -
34  PWRMGR_CONTROL_CORE_CLK_EN_BIT)),
35  "Layout of control register changed.");
36 
37 #if defined(OPENTITAN_IS_EARLGREY)
38 static_assert(kDifPwrmgrDomainOptionUsbClockInLowPower ==
39  (1u << (PWRMGR_CONTROL_USB_CLK_EN_LP_BIT -
40  PWRMGR_CONTROL_CORE_CLK_EN_BIT)),
41  "Layout of control register changed.");
42 
43 static_assert(kDifPwrmgrDomainOptionUsbClockInActivePower ==
44  (1u << (PWRMGR_CONTROL_USB_CLK_EN_ACTIVE_BIT -
45  PWRMGR_CONTROL_CORE_CLK_EN_BIT)),
46  "Layout of control register changed.");
47 #endif /* OPENTITAN_IS */
48 
49 static_assert(kDifPwrmgrDomainOptionMainPowerInLowPower ==
50  (1u << (PWRMGR_CONTROL_MAIN_PD_N_BIT -
51  PWRMGR_CONTROL_CORE_CLK_EN_BIT)),
52  "Layout of control register changed.");
53 
54 /**
55  * Bitfield for interpreting the configuration options in the control
56  * register.
57  */
58 static const bitfield_field32_t kDomainConfigBitfield = {
61 #if defined(OPENTITAN_IS_EARLGREY)
62  kDifPwrmgrDomainOptionUsbClockInLowPower |
63  kDifPwrmgrDomainOptionUsbClockInActivePower |
64 #elif defined(OPENTITAN_IS_DARJEELING)
65 /* Darjeeling has no USB clock. */
66 #else
67 #error "dif_pwrmgr does not support this top"
68 #endif
69  kDifPwrmgrDomainOptionMainPowerInLowPower,
70  .index = PWRMGR_CONTROL_CORE_CLK_EN_BIT,
71 };
72 
73 /**
74  * Relevant bits of the WAKEUP_EN and WAKE_INFO registers must start at `0` and
75  * be in the same order as `dif_pwrmgr_wakeup_request_source_t` constants.
76  */
77 #if defined(OPENTITAN_IS_EARLGREY)
78 static_assert(kDifPwrmgrWakeupRequestSourceOne ==
79  (1u << PWRMGR_WAKEUP_EN_EN_0_BIT),
80  "Layout of WAKEUP_EN register changed.");
81 static_assert(kDifPwrmgrWakeupRequestSourceOne ==
82  (1u << PWRMGR_PARAM_SYSRST_CTRL_AON_WKUP_REQ_IDX),
83  "Layout of WAKE_INFO register changed.");
84 static_assert(kDifPwrmgrWakeupRequestSourceTwo ==
85  (1u << PWRMGR_PARAM_ADC_CTRL_AON_WKUP_REQ_IDX),
86  "Layout of WAKE_INFO register changed.");
87 static_assert(kDifPwrmgrWakeupRequestSourceThree ==
88  (1u << PWRMGR_PARAM_PINMUX_AON_PIN_WKUP_REQ_IDX),
89  "Layout of WAKE_INFO register changed.");
90 static_assert(kDifPwrmgrWakeupRequestSourceFour ==
91  (1u << PWRMGR_PARAM_PINMUX_AON_USB_WKUP_REQ_IDX),
92  "Layout of WAKE_INFO register changed.");
93 static_assert(kDifPwrmgrWakeupRequestSourceFive ==
94  (1u << PWRMGR_PARAM_AON_TIMER_AON_WKUP_REQ_IDX),
95  "Layout of WAKE_INFO register changed.");
96 static_assert(kDifPwrmgrWakeupRequestSourceSix ==
97  (1u << PWRMGR_PARAM_SENSOR_CTRL_AON_WKUP_REQ_IDX),
98  "Layout of WAKE_INFO register changed.");
99 #elif defined(OPENTITAN_IS_DARJEELING)
100 static_assert(kDifPwrmgrWakeupRequestSourceOne ==
101  (1u << PWRMGR_PARAM_PINMUX_AON_PIN_WKUP_REQ_IDX),
102  "Layout of WAKE_INFO register changed.");
103 static_assert(kDifPwrmgrWakeupRequestSourceTwo ==
104  (1u << PWRMGR_PARAM_AON_TIMER_AON_WKUP_REQ_IDX),
105  "Layout of WAKE_INFO register changed.");
106 static_assert(kDifPwrmgrWakeupRequestSourceThree ==
107  (1u << PWRMGR_PARAM_SOC_PROXY_WKUP_INTERNAL_REQ_IDX),
108  "Layout of WAKE_INFO register changed.");
109 static_assert(kDifPwrmgrWakeupRequestSourceFour ==
110  (1u << PWRMGR_PARAM_SOC_PROXY_WKUP_EXTERNAL_REQ_IDX),
111  "Layout of WAKE_INFO register changed.");
112 #else
113 #error "dif_pwrmgr does not support this top"
114 #endif
115 
116 /**
117  * Relevant bits of the RESET_EN register must start at `0` and be in the same
118  * order as `dif_pwrmgr_reset_request_source_t` constants.
119  */
120 static_assert(kDifPwrmgrResetRequestSourceOne ==
121  (1u << PWRMGR_RESET_EN_EN_0_BIT),
122  "Layout of RESET_EN register changed.");
123 static_assert(kDifPwrmgrResetRequestSourceTwo ==
124  (1u << PWRMGR_RESET_EN_EN_1_BIT),
125  "Layout of RESET_EN register changed.");
126 
127 /**
128  * `dif_pwrmgr_irq_t` constants must match the corresponding generated values.
129  */
130 static_assert(kDifPwrmgrIrqWakeup == PWRMGR_INTR_COMMON_WAKEUP_BIT,
131  "Layout of interrupt registers changed.");
132 
133 /**
134  * Register information for a request type.
135  */
136 typedef struct request_reg_info {
137  ptrdiff_t write_enable_reg_offset;
138  bitfield_bit32_index_t write_enable_bit_index;
139  ptrdiff_t sources_enable_reg_offset;
140  ptrdiff_t cur_req_sources_reg_offset;
141  bitfield_field32_t bitfield;
143 
144 /**
145  * Register information for wakeup and reset requests.
146  *
147  * Wakeup and reset requests follow the same logic for configuration and
148  * monitoring but use different registers. Defining these constants here
149  * allows us to use the same code for both types of requests.
150  */
151 static const request_reg_info_t request_reg_infos[2] = {
153  {
154  .write_enable_reg_offset = PWRMGR_WAKEUP_EN_REGWEN_REG_OFFSET,
155  .write_enable_bit_index = PWRMGR_WAKEUP_EN_REGWEN_EN_BIT,
156  .sources_enable_reg_offset = PWRMGR_WAKEUP_EN_REG_OFFSET,
157  .cur_req_sources_reg_offset = PWRMGR_WAKE_STATUS_REG_OFFSET,
158  .bitfield =
159  {
160  .mask = kDifPwrmgrWakeupRequestSourceOne |
161  kDifPwrmgrWakeupRequestSourceTwo |
162  kDifPwrmgrWakeupRequestSourceThree |
163 #if defined(OPENTITAN_IS_EARLGREY)
164  kDifPwrmgrWakeupRequestSourceFour |
165  kDifPwrmgrWakeupRequestSourceFive |
166  kDifPwrmgrWakeupRequestSourceSix,
167 #elif defined(OPENTITAN_IS_DARJEELING)
168  kDifPwrmgrWakeupRequestSourceFour,
169 #else
170 #error "dif_pwrmgr does not support this top"
171 #endif
172  .index = 0,
173  },
174  },
176  {
177  .write_enable_reg_offset = PWRMGR_RESET_EN_REGWEN_REG_OFFSET,
178  .write_enable_bit_index = PWRMGR_RESET_EN_REGWEN_EN_BIT,
179  .sources_enable_reg_offset = PWRMGR_RESET_EN_REG_OFFSET,
180  .cur_req_sources_reg_offset = PWRMGR_RESET_STATUS_REG_OFFSET,
181  .bitfield =
182  {
183  .mask = kDifPwrmgrResetRequestSourceOne |
184  kDifPwrmgrResetRequestSourceTwo,
185  .index = 0,
186  },
187  },
188 };
189 
191  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
192  dt_instance_id_t inst_id, size_t sig_idx,
193  dif_pwrmgr_request_sources_t *sources) {
194  if (pwrmgr == NULL || sources == NULL) {
195  return kDifBadArg;
196  }
197  dt_pwrmgr_t dt;
198  dif_result_t res = dif_pwrmgr_get_dt(pwrmgr, &dt);
199  if (res != kDifOk) {
200  return res;
201  }
202  // Query the DT to find the information.
203  if (req_type == kDifPwrmgrReqTypeWakeup) {
204  for (size_t i = 0; i < dt_pwrmgr_wakeup_src_count(dt); i++) {
205  dt_pwrmgr_wakeup_src_t src = dt_pwrmgr_wakeup_src(dt, i);
206  if (src.inst_id == inst_id && src.wakeup == sig_idx) {
207  *sources = 1u << i;
208  return kDifOk;
209  }
210  }
211  return kDifError;
212  } else if (req_type == kDifPwrmgrReqTypeReset) {
213  for (size_t i = 0; i < dt_pwrmgr_reset_request_src_count(dt); i++) {
214  dt_pwrmgr_reset_req_src_t src = dt_pwrmgr_reset_request_src(dt, i);
215  if (src.inst_id == inst_id && src.reset_req == sig_idx) {
216  *sources = 1u << i;
217  return kDifOk;
218  }
219  }
220  return kDifError;
221  } else {
222  return kDifBadArg;
223  }
224 }
225 
226 /**
227  * Checks if a value is a valid `dif_pwrmgr_req_type_t`.
228  */
230 static bool is_valid_req_type(dif_pwrmgr_req_type_t val) {
231  return val == kDifPwrmgrReqTypeWakeup || val == kDifPwrmgrReqTypeReset;
232 }
233 
234 /**
235  * Checks if a value is valid for, i.e. fits in the mask of, a
236  * `bitfield_field32_t`.
237  */
239 static bool is_valid_for_bitfield(uint32_t val, bitfield_field32_t bitfield) {
240  return (val & bitfield.mask) == val;
241 }
242 
243 /**
244  * Checks if the control register is locked.
245  *
246  * Control register is locked when low power is enabled and WFI occurs. It is
247  * unlocked when the chip transitions back to active power state.
248  */
250 static bool control_register_is_locked(const dif_pwrmgr_t *pwrmgr) {
251  // Control register is locked when `PWRMGR_CTRL_CFG_REGWEN_EN_BIT` bit is 0.
252  return !bitfield_bit32_read(
253  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CTRL_CFG_REGWEN_REG_OFFSET),
254  PWRMGR_CTRL_CFG_REGWEN_EN_BIT);
255 }
256 
257 /**
258  * The configuration registers CONTROL, WAKEUP_EN, and RESET_EN are all written
259  * in the fast clock domain but used in the slow clock domain. Values of these
260  * registers are not propagated across the clock boundary until this function is
261  * called.
262  */
263 static void sync_slow_clock_domain_polled(const dif_pwrmgr_t *pwrmgr) {
264  // Start sync and wait for it to finish.
265  mmio_region_write32(
266  pwrmgr->base_addr, PWRMGR_CFG_CDC_SYNC_REG_OFFSET,
267  bitfield_bit32_write(0, PWRMGR_CFG_CDC_SYNC_SYNC_BIT, true));
268  while (bitfield_bit32_read(
269  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CFG_CDC_SYNC_REG_OFFSET),
270  PWRMGR_CFG_CDC_SYNC_SYNC_BIT)) {
271  }
272 }
273 
274 /**
275  * Checks if sources of a request type are locked.
276  */
278 static bool request_sources_is_locked(const dif_pwrmgr_t *pwrmgr,
279  dif_pwrmgr_req_type_t req_type) {
280  request_reg_info_t reg_info = request_reg_infos[req_type];
281  uint32_t reg_val =
282  mmio_region_read32(pwrmgr->base_addr, reg_info.write_enable_reg_offset);
283  // Locked if write enable bit is set to 0.
284  return !bitfield_bit32_read(reg_val, reg_info.write_enable_bit_index);
285 }
286 
288  dif_toggle_t new_state,
289  dif_toggle_t sync_state) {
290  if (pwrmgr == NULL || !dif_is_valid_toggle(new_state) ||
291  !dif_is_valid_toggle(sync_state)) {
292  return kDifBadArg;
293  }
294 
295  if (control_register_is_locked(pwrmgr)) {
296  return kDifLocked;
297  }
298 
299  uint32_t reg_val =
300  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
301  reg_val = bitfield_bit32_write(reg_val, PWRMGR_CONTROL_LOW_POWER_HINT_BIT,
302  dif_toggle_to_bool(new_state));
303  mmio_region_write32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET, reg_val);
304 
305  // Slow clock domain may be synced for changes to take effect.
306  if (sync_state == kDifToggleEnabled)
307  sync_slow_clock_domain_polled(pwrmgr);
308 
309  return kDifOk;
310 }
311 
313  dif_toggle_t *cur_state) {
314  if (pwrmgr == NULL || cur_state == NULL) {
315  return kDifBadArg;
316  }
317 
318  uint32_t reg_val =
319  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
320  *cur_state = dif_bool_to_toggle(
321  bitfield_bit32_read(reg_val, PWRMGR_CONTROL_LOW_POWER_HINT_BIT));
322 
323  return kDifOk;
324 }
325 
326 dif_result_t dif_pwrmgr_set_domain_config(const dif_pwrmgr_t *pwrmgr,
328  dif_toggle_t sync_state) {
329  if (pwrmgr == NULL || !is_valid_for_bitfield(config, kDomainConfigBitfield) ||
330  !dif_is_valid_toggle(sync_state)) {
331  return kDifBadArg;
332  }
333 
334  if (control_register_is_locked(pwrmgr)) {
335  return kDifLocked;
336  }
337 
338  uint32_t reg_val =
339  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
340  reg_val = bitfield_field32_write(reg_val, kDomainConfigBitfield, config);
341  mmio_region_write32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET, reg_val);
342 
343  // Slow clock domain must be synced for changes to take effect.
344  if (sync_state == kDifToggleEnabled)
345  sync_slow_clock_domain_polled(pwrmgr);
346 
347  return kDifOk;
348 }
349 
350 dif_result_t dif_pwrmgr_get_domain_config(const dif_pwrmgr_t *pwrmgr,
351  dif_pwrmgr_domain_config_t *config) {
352  if (pwrmgr == NULL || config == NULL) {
353  return kDifBadArg;
354  }
355 
356  uint32_t reg_val =
357  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
359  reg_val, kDomainConfigBitfield);
360 
361  return kDifOk;
362 }
363 
365  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
366  dif_pwrmgr_request_sources_t sources, dif_toggle_t sync_state) {
367  if (pwrmgr == NULL || !is_valid_req_type(req_type) ||
368  !dif_is_valid_toggle(sync_state)) {
369  return kDifBadArg;
370  }
371 
372  request_reg_info_t reg_info = request_reg_infos[req_type];
373 
374  if (!is_valid_for_bitfield(sources, reg_info.bitfield)) {
375  return kDifBadArg;
376  }
377 
378  // Return early if locked.
379  if (request_sources_is_locked(pwrmgr, req_type)) {
380  return kDifLocked;
381  }
382 
383  // Write new value
384  uint32_t reg_val = bitfield_field32_write(0, reg_info.bitfield, sources);
385  mmio_region_write32(pwrmgr->base_addr, reg_info.sources_enable_reg_offset,
386  reg_val);
387  // Slow clock domain may be synced for changes to take effect.
388  if (sync_state == kDifToggleEnabled)
389  sync_slow_clock_domain_polled(pwrmgr);
390 
391  return kDifOk;
392 }
393 
395  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
396  dif_pwrmgr_request_sources_t *sources) {
397  if (pwrmgr == NULL || !is_valid_req_type(req_type) || sources == NULL) {
398  return kDifBadArg;
399  }
400 
401  request_reg_info_t reg_info = request_reg_infos[req_type];
402  uint32_t reg_val =
403  mmio_region_read32(pwrmgr->base_addr, reg_info.sources_enable_reg_offset);
404  *sources = bitfield_field32_read(reg_val, reg_info.bitfield);
405 
406  return kDifOk;
407 }
408 
410  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
411  dif_pwrmgr_request_sources_t *sources) {
412  if (pwrmgr == NULL || !is_valid_req_type(req_type) || sources == NULL) {
413  return kDifBadArg;
414  }
415 
416  request_reg_info_t reg_info = request_reg_infos[req_type];
417  uint32_t reg_val = mmio_region_read32(pwrmgr->base_addr,
418  reg_info.cur_req_sources_reg_offset);
419  *sources = bitfield_field32_read(reg_val, reg_info.bitfield);
420 
421  return kDifOk;
422 }
423 
425  dif_pwrmgr_req_type_t req_type) {
426  if (pwrmgr == NULL || !is_valid_req_type(req_type)) {
427  return kDifBadArg;
428  }
429 
430  // Only a single bit of this register is significant, thus we don't perform a
431  // read-modify-write. Setting this bit to 0 locks sources.
432  mmio_region_write32(pwrmgr->base_addr,
433  request_reg_infos[req_type].write_enable_reg_offset, 0);
434 
435  return kDifOk;
436 }
437 
439  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
440  bool *is_locked) {
441  if (pwrmgr == NULL || !is_valid_req_type(req_type) || is_locked == NULL) {
442  return kDifBadArg;
443  }
444 
445  *is_locked = request_sources_is_locked(pwrmgr, req_type);
446 
447  return kDifOk;
448 }
449 
451  const dif_pwrmgr_t *pwrmgr, dif_toggle_t new_state) {
452  if (pwrmgr == NULL || !dif_is_valid_toggle(new_state)) {
453  return kDifBadArg;
454  }
455 
456  // Only a single bit of this register is significant, thus we don't perform a
457  // read-modify-write. Setting this bit to 1 disables recording.
458  uint32_t reg_val = bitfield_bit32_write(
459  0, PWRMGR_WAKE_INFO_CAPTURE_DIS_VAL_BIT, !dif_toggle_to_bool(new_state));
460  mmio_region_write32(pwrmgr->base_addr,
461  PWRMGR_WAKE_INFO_CAPTURE_DIS_REG_OFFSET, reg_val);
462 
463  return kDifOk;
464 }
465 
467  const dif_pwrmgr_t *pwrmgr, dif_toggle_t *cur_state) {
468  if (pwrmgr == NULL || cur_state == NULL) {
469  return kDifBadArg;
470  }
471 
472  uint32_t reg_val = mmio_region_read32(
473  pwrmgr->base_addr, PWRMGR_WAKE_INFO_CAPTURE_DIS_REG_OFFSET);
474  // Recording is disabled if this bit is set to 1.
475  *cur_state = dif_bool_to_toggle(
476  !bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_CAPTURE_DIS_VAL_BIT));
477 
478  return kDifOk;
479 }
480 
481 dif_result_t dif_pwrmgr_wakeup_reason_get(const dif_pwrmgr_t *pwrmgr,
482  dif_pwrmgr_wakeup_reason_t *reason) {
483  if (pwrmgr == NULL || reason == NULL) {
484  return kDifBadArg;
485  }
486 
487  uint32_t reg_val =
488  mmio_region_read32(pwrmgr->base_addr, PWRMGR_WAKE_INFO_REG_OFFSET);
489 
490  dif_pwrmgr_wakeup_types_t types = 0;
491  if (bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_FALL_THROUGH_BIT)) {
493  }
494  if (bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_ABORT_BIT)) {
495  types |= kDifPwrmgrWakeupTypeAbort;
496  }
497 
498  uint32_t request_sources = bitfield_field32_read(
499  reg_val, request_reg_infos[kDifPwrmgrReqTypeWakeup].bitfield);
500  if (request_sources != 0) {
502  }
503 
504  *reason = (dif_pwrmgr_wakeup_reason_t){
505  .types = types,
506  .request_sources = request_sources,
507  };
508 
509  return kDifOk;
510 }
511 
512 dif_result_t dif_pwrmgr_wakeup_reason_clear(const dif_pwrmgr_t *pwrmgr) {
513  if (pwrmgr == NULL) {
514  return kDifBadArg;
515  }
516 
517  mmio_region_write32(pwrmgr->base_addr, PWRMGR_WAKE_INFO_REG_OFFSET,
518  UINT32_MAX);
519 
520  return kDifOk;
521 }
522 
524  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_fatal_err_codes_t *codes) {
525  if (pwrmgr == NULL || codes == NULL) {
526  return kDifBadArg;
527  }
528  *codes =
529  mmio_region_read32(pwrmgr->base_addr, PWRMGR_FAULT_STATUS_REG_OFFSET);
530  return kDifOk;
531 }