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 
182 /**
183  * Checks if a value is a valid `dif_pwrmgr_req_type_t`.
184  */
186 static bool is_valid_req_type(dif_pwrmgr_req_type_t val) {
187  return val == kDifPwrmgrReqTypeWakeup || val == kDifPwrmgrReqTypeReset;
188 }
189 
190 /**
191  * Checks if a value is valid for, i.e. fits in the mask of, a
192  * `bitfield_field32_t`.
193  */
195 static bool is_valid_for_bitfield(uint32_t val, bitfield_field32_t bitfield) {
196  return (val & bitfield.mask) == val;
197 }
198 
199 /**
200  * Checks if the control register is locked.
201  *
202  * Control register is locked when low power is enabled and WFI occurs. It is
203  * unlocked when the chip transitions back to active power state.
204  */
206 static bool control_register_is_locked(const dif_pwrmgr_t *pwrmgr) {
207  // Control register is locked when `PWRMGR_CTRL_CFG_REGWEN_EN_BIT` bit is 0.
208  return !bitfield_bit32_read(
209  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CTRL_CFG_REGWEN_REG_OFFSET),
210  PWRMGR_CTRL_CFG_REGWEN_EN_BIT);
211 }
212 
213 /**
214  * The configuration registers CONTROL, WAKEUP_EN, and RESET_EN are all written
215  * in the fast clock domain but used in the slow clock domain. Values of these
216  * registers are not propagated across the clock boundary until this function is
217  * called.
218  */
219 static void sync_slow_clock_domain_polled(const dif_pwrmgr_t *pwrmgr) {
220  // Start sync and wait for it to finish.
221  mmio_region_write32(
222  pwrmgr->base_addr, PWRMGR_CFG_CDC_SYNC_REG_OFFSET,
223  bitfield_bit32_write(0, PWRMGR_CFG_CDC_SYNC_SYNC_BIT, true));
224  while (bitfield_bit32_read(
225  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CFG_CDC_SYNC_REG_OFFSET),
226  PWRMGR_CFG_CDC_SYNC_SYNC_BIT)) {
227  }
228 }
229 
230 /**
231  * Checks if sources of a request type are locked.
232  */
234 static bool request_sources_is_locked(const dif_pwrmgr_t *pwrmgr,
235  dif_pwrmgr_req_type_t req_type) {
236  request_reg_info_t reg_info = request_reg_infos[req_type];
237  uint32_t reg_val =
238  mmio_region_read32(pwrmgr->base_addr, reg_info.write_enable_reg_offset);
239  // Locked if write enable bit is set to 0.
240  return !bitfield_bit32_read(reg_val, reg_info.write_enable_bit_index);
241 }
242 
244  dif_toggle_t new_state,
245  dif_toggle_t sync_state) {
246  if (pwrmgr == NULL || !dif_is_valid_toggle(new_state) ||
247  !dif_is_valid_toggle(sync_state)) {
248  return kDifBadArg;
249  }
250 
251  if (control_register_is_locked(pwrmgr)) {
252  return kDifLocked;
253  }
254 
255  uint32_t reg_val =
256  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
257  reg_val = bitfield_bit32_write(reg_val, PWRMGR_CONTROL_LOW_POWER_HINT_BIT,
258  dif_toggle_to_bool(new_state));
259  mmio_region_write32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET, reg_val);
260 
261  // Slow clock domain may be synced for changes to take effect.
262  if (sync_state == kDifToggleEnabled)
263  sync_slow_clock_domain_polled(pwrmgr);
264 
265  return kDifOk;
266 }
267 
269  dif_toggle_t *cur_state) {
270  if (pwrmgr == NULL || cur_state == NULL) {
271  return kDifBadArg;
272  }
273 
274  uint32_t reg_val =
275  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
276  *cur_state = dif_bool_to_toggle(
277  bitfield_bit32_read(reg_val, PWRMGR_CONTROL_LOW_POWER_HINT_BIT));
278 
279  return kDifOk;
280 }
281 
282 dif_result_t dif_pwrmgr_set_domain_config(const dif_pwrmgr_t *pwrmgr,
284  dif_toggle_t sync_state) {
285  if (pwrmgr == NULL || !is_valid_for_bitfield(config, kDomainConfigBitfield) ||
286  !dif_is_valid_toggle(sync_state)) {
287  return kDifBadArg;
288  }
289 
290  if (control_register_is_locked(pwrmgr)) {
291  return kDifLocked;
292  }
293 
294  uint32_t reg_val =
295  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
296  reg_val = bitfield_field32_write(reg_val, kDomainConfigBitfield, config);
297  mmio_region_write32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET, reg_val);
298 
299  // Slow clock domain may be synced for changes to take effect.
300  if (sync_state == kDifToggleEnabled)
301  sync_slow_clock_domain_polled(pwrmgr);
302 
303  return kDifOk;
304 }
305 
306 dif_result_t dif_pwrmgr_get_domain_config(const dif_pwrmgr_t *pwrmgr,
307  dif_pwrmgr_domain_config_t *config) {
308  if (pwrmgr == NULL || config == NULL) {
309  return kDifBadArg;
310  }
311 
312  uint32_t reg_val =
313  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
315  reg_val, kDomainConfigBitfield);
316 
317  return kDifOk;
318 }
319 
321  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
322  dif_pwrmgr_request_sources_t sources, dif_toggle_t sync_state) {
323  if (pwrmgr == NULL || !is_valid_req_type(req_type) ||
324  !dif_is_valid_toggle(sync_state)) {
325  return kDifBadArg;
326  }
327 
328  request_reg_info_t reg_info = request_reg_infos[req_type];
329 
330  if (!is_valid_for_bitfield(sources, reg_info.bitfield)) {
331  return kDifBadArg;
332  }
333 
334  // Return early if locked.
335  if (request_sources_is_locked(pwrmgr, req_type)) {
336  return kDifLocked;
337  }
338 
339  // Write new value
340  uint32_t reg_val = bitfield_field32_write(0, reg_info.bitfield, sources);
341  mmio_region_write32(pwrmgr->base_addr, reg_info.sources_enable_reg_offset,
342  reg_val);
343  // Slow clock domain may 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 
351  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
352  dif_pwrmgr_request_sources_t *sources) {
353  if (pwrmgr == NULL || !is_valid_req_type(req_type) || sources == NULL) {
354  return kDifBadArg;
355  }
356 
357  request_reg_info_t reg_info = request_reg_infos[req_type];
358  uint32_t reg_val =
359  mmio_region_read32(pwrmgr->base_addr, reg_info.sources_enable_reg_offset);
360  *sources = bitfield_field32_read(reg_val, reg_info.bitfield);
361 
362  return kDifOk;
363 }
364 
366  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
367  dif_pwrmgr_request_sources_t *sources) {
368  if (pwrmgr == NULL || !is_valid_req_type(req_type) || sources == NULL) {
369  return kDifBadArg;
370  }
371 
372  request_reg_info_t reg_info = request_reg_infos[req_type];
373  uint32_t reg_val = mmio_region_read32(pwrmgr->base_addr,
374  reg_info.cur_req_sources_reg_offset);
375  *sources = bitfield_field32_read(reg_val, reg_info.bitfield);
376 
377  return kDifOk;
378 }
379 
381  dif_pwrmgr_req_type_t req_type) {
382  if (pwrmgr == NULL || !is_valid_req_type(req_type)) {
383  return kDifBadArg;
384  }
385 
386  // Only a single bit of this register is significant, thus we don't perform a
387  // read-modify-write. Setting this bit to 0 locks sources.
388  mmio_region_write32(pwrmgr->base_addr,
389  request_reg_infos[req_type].write_enable_reg_offset, 0);
390 
391  return kDifOk;
392 }
393 
395  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
396  bool *is_locked) {
397  if (pwrmgr == NULL || !is_valid_req_type(req_type) || is_locked == NULL) {
398  return kDifBadArg;
399  }
400 
401  *is_locked = request_sources_is_locked(pwrmgr, req_type);
402 
403  return kDifOk;
404 }
405 
407  const dif_pwrmgr_t *pwrmgr, dif_toggle_t new_state) {
408  if (pwrmgr == NULL || !dif_is_valid_toggle(new_state)) {
409  return kDifBadArg;
410  }
411 
412  // Only a single bit of this register is significant, thus we don't perform a
413  // read-modify-write. Setting this bit to 1 disables recording.
414  uint32_t reg_val = bitfield_bit32_write(
415  0, PWRMGR_WAKE_INFO_CAPTURE_DIS_VAL_BIT, !dif_toggle_to_bool(new_state));
416  mmio_region_write32(pwrmgr->base_addr,
417  PWRMGR_WAKE_INFO_CAPTURE_DIS_REG_OFFSET, reg_val);
418 
419  return kDifOk;
420 }
421 
423  const dif_pwrmgr_t *pwrmgr, dif_toggle_t *cur_state) {
424  if (pwrmgr == NULL || cur_state == NULL) {
425  return kDifBadArg;
426  }
427 
428  uint32_t reg_val = mmio_region_read32(
429  pwrmgr->base_addr, PWRMGR_WAKE_INFO_CAPTURE_DIS_REG_OFFSET);
430  // Recording is disabled if this bit is set to 1.
431  *cur_state = dif_bool_to_toggle(
432  !bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_CAPTURE_DIS_VAL_BIT));
433 
434  return kDifOk;
435 }
436 
437 dif_result_t dif_pwrmgr_wakeup_reason_get(const dif_pwrmgr_t *pwrmgr,
438  dif_pwrmgr_wakeup_reason_t *reason) {
439  if (pwrmgr == NULL || reason == NULL) {
440  return kDifBadArg;
441  }
442 
443  uint32_t reg_val =
444  mmio_region_read32(pwrmgr->base_addr, PWRMGR_WAKE_INFO_REG_OFFSET);
445 
446  dif_pwrmgr_wakeup_types_t types = 0;
447  if (bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_FALL_THROUGH_BIT)) {
449  }
450  if (bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_ABORT_BIT)) {
451  types |= kDifPwrmgrWakeupTypeAbort;
452  }
453 
454  uint32_t request_sources = bitfield_field32_read(
455  reg_val, request_reg_infos[kDifPwrmgrReqTypeWakeup].bitfield);
456  if (request_sources != 0) {
458  }
459 
460  *reason = (dif_pwrmgr_wakeup_reason_t){
461  .types = types,
462  .request_sources = request_sources,
463  };
464 
465  return kDifOk;
466 }
467 
468 dif_result_t dif_pwrmgr_wakeup_reason_clear(const dif_pwrmgr_t *pwrmgr) {
469  if (pwrmgr == NULL) {
470  return kDifBadArg;
471  }
472 
473  mmio_region_write32(pwrmgr->base_addr, PWRMGR_WAKE_INFO_REG_OFFSET,
474  UINT32_MAX);
475 
476  return kDifOk;
477 }
478 
480  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_fatal_err_codes_t *codes) {
481  if (pwrmgr == NULL || codes == NULL) {
482  return kDifBadArg;
483  }
484  *codes =
485  mmio_region_read32(pwrmgr->base_addr, PWRMGR_FAULT_STATUS_REG_OFFSET);
486  return kDifOk;
487 }