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 static_assert(kDifPwrmgrWakeupRequestSourceOne ==
70  (1u << PWRMGR_WAKEUP_EN_EN_0_BIT),
71  "Layout of WAKEUP_EN register changed.");
72 static_assert(kDifPwrmgrWakeupRequestSourceOne ==
73  (1u << PWRMGR_PARAM_SYSRST_CTRL_AON_WKUP_REQ_IDX),
74  "Layout of WAKE_INFO register changed.");
75 static_assert(kDifPwrmgrWakeupRequestSourceTwo ==
76  (1u << PWRMGR_PARAM_ADC_CTRL_AON_WKUP_REQ_IDX),
77  "Layout of WAKE_INFO register changed.");
78 static_assert(kDifPwrmgrWakeupRequestSourceThree ==
79  (1u << PWRMGR_PARAM_PINMUX_AON_PIN_WKUP_REQ_IDX),
80  "Layout of WAKE_INFO register changed.");
81 static_assert(kDifPwrmgrWakeupRequestSourceFour ==
82  (1u << PWRMGR_PARAM_PINMUX_AON_USB_WKUP_REQ_IDX),
83  "Layout of WAKE_INFO register changed.");
84 static_assert(kDifPwrmgrWakeupRequestSourceFive ==
85  (1u << PWRMGR_PARAM_AON_TIMER_AON_WKUP_REQ_IDX),
86  "Layout of WAKE_INFO register changed.");
87 static_assert(kDifPwrmgrWakeupRequestSourceSix ==
88  (1u << PWRMGR_PARAM_SENSOR_CTRL_AON_WKUP_REQ_IDX),
89  "Layout of WAKE_INFO register changed.");
90 
91 /**
92  * Relevant bits of the RESET_EN register must start at `0` and be in the same
93  * order as `dif_pwrmgr_reset_request_source_t` constants.
94  */
95 static_assert(kDifPwrmgrResetRequestSourceOne ==
96  (1u << PWRMGR_RESET_EN_EN_0_BIT),
97  "Layout of RESET_EN register changed.");
98 static_assert(kDifPwrmgrResetRequestSourceTwo ==
99  (1u << PWRMGR_RESET_EN_EN_1_BIT),
100  "Layout of RESET_EN register changed.");
101 
102 /**
103  * `dif_pwrmgr_irq_t` constants must match the corresponding generated values.
104  */
105 static_assert(kDifPwrmgrIrqWakeup == PWRMGR_INTR_COMMON_WAKEUP_BIT,
106  "Layout of interrupt registers changed.");
107 
108 /**
109  * Register information for a request type.
110  */
111 typedef struct request_reg_info {
112  ptrdiff_t write_enable_reg_offset;
113  bitfield_bit32_index_t write_enable_bit_index;
114  ptrdiff_t sources_enable_reg_offset;
115  ptrdiff_t cur_req_sources_reg_offset;
116  bitfield_field32_t bitfield;
118 
119 /**
120  * Register information for wakeup and reset requests.
121  *
122  * Wakeup and reset requests follow the same logic for configuration and
123  * monitoring but use different registers. Defining these constants here
124  * allows us to use the same code for both types of requests.
125  */
126 static const request_reg_info_t request_reg_infos[2] = {
128  {
129  .write_enable_reg_offset = PWRMGR_WAKEUP_EN_REGWEN_REG_OFFSET,
130  .write_enable_bit_index = PWRMGR_WAKEUP_EN_REGWEN_EN_BIT,
131  .sources_enable_reg_offset = PWRMGR_WAKEUP_EN_REG_OFFSET,
132  .cur_req_sources_reg_offset = PWRMGR_WAKE_STATUS_REG_OFFSET,
133  .bitfield =
134  {
135  .mask = kDifPwrmgrWakeupRequestSourceOne |
136  kDifPwrmgrWakeupRequestSourceTwo |
137  kDifPwrmgrWakeupRequestSourceThree |
138  kDifPwrmgrWakeupRequestSourceFour |
139  kDifPwrmgrWakeupRequestSourceFive |
140  kDifPwrmgrWakeupRequestSourceSix,
141  .index = 0,
142  },
143  },
145  {
146  .write_enable_reg_offset = PWRMGR_RESET_EN_REGWEN_REG_OFFSET,
147  .write_enable_bit_index = PWRMGR_RESET_EN_REGWEN_EN_BIT,
148  .sources_enable_reg_offset = PWRMGR_RESET_EN_REG_OFFSET,
149  .cur_req_sources_reg_offset = PWRMGR_RESET_STATUS_REG_OFFSET,
150  .bitfield =
151  {
152  .mask = kDifPwrmgrResetRequestSourceOne |
153  kDifPwrmgrResetRequestSourceTwo,
154  .index = 0,
155  },
156  },
157 };
158 
159 /**
160  * Checks if a value is a valid `dif_pwrmgr_req_type_t`.
161  */
163 static bool is_valid_req_type(dif_pwrmgr_req_type_t val) {
164  return val == kDifPwrmgrReqTypeWakeup || val == kDifPwrmgrReqTypeReset;
165 }
166 
167 /**
168  * Checks if a value is valid for, i.e. fits in the mask of, a
169  * `bitfield_field32_t`.
170  */
172 static bool is_valid_for_bitfield(uint32_t val, bitfield_field32_t bitfield) {
173  return (val & bitfield.mask) == val;
174 }
175 
176 /**
177  * Checks if the control register is locked.
178  *
179  * Control register is locked when low power is enabled and WFI occurs. It is
180  * unlocked when the chip transitions back to active power state.
181  */
183 static bool control_register_is_locked(const dif_pwrmgr_t *pwrmgr) {
184  // Control register is locked when `PWRMGR_CTRL_CFG_REGWEN_EN_BIT` bit is 0.
185  return !bitfield_bit32_read(
186  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CTRL_CFG_REGWEN_REG_OFFSET),
187  PWRMGR_CTRL_CFG_REGWEN_EN_BIT);
188 }
189 
190 /**
191  * The configuration registers CONTROL, WAKEUP_EN, and RESET_EN are all written
192  * in the fast clock domain but used in the slow clock domain. Values of these
193  * registers are not propagated across the clock boundary until this function is
194  * called.
195  */
196 static void sync_slow_clock_domain_polled(const dif_pwrmgr_t *pwrmgr) {
197  // Start sync and wait for it to finish.
198  mmio_region_write32(
199  pwrmgr->base_addr, PWRMGR_CFG_CDC_SYNC_REG_OFFSET,
200  bitfield_bit32_write(0, PWRMGR_CFG_CDC_SYNC_SYNC_BIT, true));
201  while (bitfield_bit32_read(
202  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CFG_CDC_SYNC_REG_OFFSET),
203  PWRMGR_CFG_CDC_SYNC_SYNC_BIT)) {
204  }
205 }
206 
207 /**
208  * Checks if sources of a request type are locked.
209  */
211 static bool request_sources_is_locked(const dif_pwrmgr_t *pwrmgr,
212  dif_pwrmgr_req_type_t req_type) {
213  request_reg_info_t reg_info = request_reg_infos[req_type];
214  uint32_t reg_val =
215  mmio_region_read32(pwrmgr->base_addr, reg_info.write_enable_reg_offset);
216  // Locked if write enable bit is set to 0.
217  return !bitfield_bit32_read(reg_val, reg_info.write_enable_bit_index);
218 }
219 
221  dif_toggle_t new_state,
222  dif_toggle_t sync_state) {
223  if (pwrmgr == NULL || !dif_is_valid_toggle(new_state) ||
224  !dif_is_valid_toggle(sync_state)) {
225  return kDifBadArg;
226  }
227 
228  if (control_register_is_locked(pwrmgr)) {
229  return kDifLocked;
230  }
231 
232  uint32_t reg_val =
233  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
234  reg_val = bitfield_bit32_write(reg_val, PWRMGR_CONTROL_LOW_POWER_HINT_BIT,
235  dif_toggle_to_bool(new_state));
236  mmio_region_write32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET, reg_val);
237 
238  // Slow clock domain may be synced for changes to take effect.
239  if (sync_state == kDifToggleEnabled)
240  sync_slow_clock_domain_polled(pwrmgr);
241 
242  return kDifOk;
243 }
244 
246  dif_toggle_t *cur_state) {
247  if (pwrmgr == NULL || cur_state == NULL) {
248  return kDifBadArg;
249  }
250 
251  uint32_t reg_val =
252  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
253  *cur_state = dif_bool_to_toggle(
254  bitfield_bit32_read(reg_val, PWRMGR_CONTROL_LOW_POWER_HINT_BIT));
255 
256  return kDifOk;
257 }
258 
261  dif_toggle_t sync_state) {
262  if (pwrmgr == NULL || !is_valid_for_bitfield(config, kDomainConfigBitfield) ||
263  !dif_is_valid_toggle(sync_state)) {
264  return kDifBadArg;
265  }
266 
267  if (control_register_is_locked(pwrmgr)) {
268  return kDifLocked;
269  }
270 
271  uint32_t reg_val =
272  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
273  reg_val = bitfield_field32_write(reg_val, kDomainConfigBitfield, config);
274  mmio_region_write32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET, reg_val);
275 
276  // Slow clock domain may be synced for changes to take effect.
277  if (sync_state == kDifToggleEnabled)
278  sync_slow_clock_domain_polled(pwrmgr);
279 
280  return kDifOk;
281 }
282 
284  dif_pwrmgr_domain_config_t *config) {
285  if (pwrmgr == NULL || config == NULL) {
286  return kDifBadArg;
287  }
288 
289  uint32_t reg_val =
290  mmio_region_read32(pwrmgr->base_addr, PWRMGR_CONTROL_REG_OFFSET);
292  reg_val, kDomainConfigBitfield);
293 
294  return kDifOk;
295 }
296 
298  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
299  dif_pwrmgr_request_sources_t sources, dif_toggle_t sync_state) {
300  if (pwrmgr == NULL || !is_valid_req_type(req_type) ||
301  !dif_is_valid_toggle(sync_state)) {
302  return kDifBadArg;
303  }
304 
305  request_reg_info_t reg_info = request_reg_infos[req_type];
306 
307  if (!is_valid_for_bitfield(sources, reg_info.bitfield)) {
308  return kDifBadArg;
309  }
310 
311  // Return early if locked.
312  if (request_sources_is_locked(pwrmgr, req_type)) {
313  return kDifLocked;
314  }
315 
316  // Write new value
317  uint32_t reg_val = bitfield_field32_write(0, reg_info.bitfield, sources);
318  mmio_region_write32(pwrmgr->base_addr, reg_info.sources_enable_reg_offset,
319  reg_val);
320  // Slow clock domain may be synced for changes to take effect.
321  if (sync_state == kDifToggleEnabled)
322  sync_slow_clock_domain_polled(pwrmgr);
323 
324  return kDifOk;
325 }
326 
328  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
329  dif_pwrmgr_request_sources_t *sources) {
330  if (pwrmgr == NULL || !is_valid_req_type(req_type) || sources == NULL) {
331  return kDifBadArg;
332  }
333 
334  request_reg_info_t reg_info = request_reg_infos[req_type];
335  uint32_t reg_val =
336  mmio_region_read32(pwrmgr->base_addr, reg_info.sources_enable_reg_offset);
337  *sources = bitfield_field32_read(reg_val, reg_info.bitfield);
338 
339  return kDifOk;
340 }
341 
343  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
344  dif_pwrmgr_request_sources_t *sources) {
345  if (pwrmgr == NULL || !is_valid_req_type(req_type) || sources == NULL) {
346  return kDifBadArg;
347  }
348 
349  request_reg_info_t reg_info = request_reg_infos[req_type];
350  uint32_t reg_val = mmio_region_read32(pwrmgr->base_addr,
351  reg_info.cur_req_sources_reg_offset);
352  *sources = bitfield_field32_read(reg_val, reg_info.bitfield);
353 
354  return kDifOk;
355 }
356 
358  dif_pwrmgr_req_type_t req_type) {
359  if (pwrmgr == NULL || !is_valid_req_type(req_type)) {
360  return kDifBadArg;
361  }
362 
363  // Only a single bit of this register is significant, thus we don't perform a
364  // read-modify-write. Setting this bit to 0 locks sources.
365  mmio_region_write32(pwrmgr->base_addr,
366  request_reg_infos[req_type].write_enable_reg_offset, 0);
367 
368  return kDifOk;
369 }
370 
372  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
373  bool *is_locked) {
374  if (pwrmgr == NULL || !is_valid_req_type(req_type) || is_locked == NULL) {
375  return kDifBadArg;
376  }
377 
378  *is_locked = request_sources_is_locked(pwrmgr, req_type);
379 
380  return kDifOk;
381 }
382 
384  const dif_pwrmgr_t *pwrmgr, dif_toggle_t new_state) {
385  if (pwrmgr == NULL || !dif_is_valid_toggle(new_state)) {
386  return kDifBadArg;
387  }
388 
389  // Only a single bit of this register is significant, thus we don't perform a
390  // read-modify-write. Setting this bit to 1 disables recording.
391  uint32_t reg_val = bitfield_bit32_write(
392  0, PWRMGR_WAKE_INFO_CAPTURE_DIS_VAL_BIT, !dif_toggle_to_bool(new_state));
393  mmio_region_write32(pwrmgr->base_addr,
394  PWRMGR_WAKE_INFO_CAPTURE_DIS_REG_OFFSET, reg_val);
395 
396  return kDifOk;
397 }
398 
400  const dif_pwrmgr_t *pwrmgr, dif_toggle_t *cur_state) {
401  if (pwrmgr == NULL || cur_state == NULL) {
402  return kDifBadArg;
403  }
404 
405  uint32_t reg_val = mmio_region_read32(
406  pwrmgr->base_addr, PWRMGR_WAKE_INFO_CAPTURE_DIS_REG_OFFSET);
407  // Recording is disabled if this bit is set to 1.
408  *cur_state = dif_bool_to_toggle(
409  !bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_CAPTURE_DIS_VAL_BIT));
410 
411  return kDifOk;
412 }
413 
415  dif_pwrmgr_wakeup_reason_t *reason) {
416  if (pwrmgr == NULL || reason == NULL) {
417  return kDifBadArg;
418  }
419 
420  uint32_t reg_val =
421  mmio_region_read32(pwrmgr->base_addr, PWRMGR_WAKE_INFO_REG_OFFSET);
422 
423  dif_pwrmgr_wakeup_types_t types = 0;
424  if (bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_FALL_THROUGH_BIT)) {
426  }
427  if (bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_ABORT_BIT)) {
428  types |= kDifPwrmgrWakeupTypeAbort;
429  }
430 
431  uint32_t request_sources = bitfield_field32_read(
432  reg_val, request_reg_infos[kDifPwrmgrReqTypeWakeup].bitfield);
433  if (request_sources != 0) {
435  }
436 
437  *reason = (dif_pwrmgr_wakeup_reason_t){
438  .types = types,
439  .request_sources = request_sources,
440  };
441 
442  return kDifOk;
443 }
444 
446  if (pwrmgr == NULL) {
447  return kDifBadArg;
448  }
449 
450  mmio_region_write32(pwrmgr->base_addr, PWRMGR_WAKE_INFO_REG_OFFSET,
451  UINT32_MAX);
452 
453  return kDifOk;
454 }
455 
457  const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_fatal_err_codes_t *codes) {
458  if (pwrmgr == NULL || codes == NULL) {
459  return kDifBadArg;
460  }
461  *codes =
462  mmio_region_read32(pwrmgr->base_addr, PWRMGR_FAULT_STATUS_REG_OFFSET);
463  return kDifOk;
464 }