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