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