lowrisc/
pwrmgr.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Power Mangement for LowRISC
6
7use kernel::utilities::registers::interfaces::{Readable, Writeable};
8use kernel::utilities::registers::{register_bitfields, register_structs, ReadOnly, ReadWrite};
9use kernel::utilities::StaticRef;
10
11register_structs! {
12    pub PwrMgrRegisters {
13        (0x00 => intr_state: ReadOnly<u32, INTR::Register>),
14        (0x04 => intr_enable: ReadOnly<u32, INTR::Register>),
15        (0x08 => intr_test: ReadOnly<u32, INTR::Register>),
16        (0x0C => ctrl_cfg_regwen: ReadOnly<u32, CTRL_CFG_REGWEN::Register>),
17        (0x10 => control: ReadWrite<u32, CONTROL::Register>),
18        (0x14 => cfg_cdc_sync: ReadWrite<u32, CFG_CDC_SYNC::Register>),
19        (0x18 => wakeup_en_regwen: ReadWrite<u32, WAKEUP_EN_REGWEN::Register>),
20        (0x1C => wakeup_en: ReadWrite<u32, WAKEUP_EN::Register>),
21        (0x20 => wake_status: ReadOnly<u32, WAKE_STATUS::Register>),
22        (0x24 => reset_en_regwen: ReadWrite<u32, RESET_EN_REGWEN::Register>),
23        (0x28 => reset_en: ReadWrite<u32, RESET_EN::Register>),
24        (0x2C => reset_status: ReadOnly<u32, RESET_STATUS::Register>),
25        (0x30 => escalate_reset_status: ReadOnly<u32>),
26        (0x34 => wake_info_capture_dis: ReadWrite<u32, WAKE_INFO_CAPTURE_DIS::Register>),
27        (0x38 => wake_info: ReadWrite<u32, WAKE_INFO::Register>),
28        (0x3C => @END),
29    }
30}
31
32register_bitfields![u32,
33    INTR [
34        WAKEUP OFFSET(0) NUMBITS(1) []
35    ],
36    CTRL_CFG_REGWEN [
37        EN OFFSET(0) NUMBITS(1) []
38    ],
39    CONTROL [
40        LOW_POWER_HINT OFFSET(0) NUMBITS(1) [],
41        CORE_CLK_EN OFFSET(4) NUMBITS(1) [],
42        IO_CLK_EN OFFSET(5) NUMBITS(1) [],
43        USB_CLKC_EN_LP OFFSET(6) NUMBITS(1) [],
44        USB_CLK_EN_ACTIVE OFFSET(7) NUMBITS(1) [],
45        MAIN_PD_N OFFSET(8) NUMBITS(1) [],
46    ],
47    CFG_CDC_SYNC [
48        SYNC OFFSET(0) NUMBITS(1) []
49    ],
50    WAKEUP_EN_REGWEN [
51        EN OFFSET(0) NUMBITS(1) []
52    ],
53    WAKEUP_EN [
54        EN0 OFFSET(0) NUMBITS(1) [],
55        EN1 OFFSET(1) NUMBITS(1) [],
56        EN2 OFFSET(2) NUMBITS(1) [],
57        EN3 OFFSET(3) NUMBITS(1) [],
58        EN4 OFFSET(4) NUMBITS(1) [],
59    ],
60    WAKE_STATUS [
61        VAL0 OFFSET(0) NUMBITS(1) [],
62        VAL1 OFFSET(1) NUMBITS(1) [],
63        VAL2 OFFSET(2) NUMBITS(1) [],
64        VAL3 OFFSET(3) NUMBITS(1) [],
65        VAL4 OFFSET(4) NUMBITS(1) [],
66    ],
67    RESET_EN_REGWEN [
68        EN OFFSET(0) NUMBITS(1) []
69    ],
70    RESET_EN [
71        EN0 OFFSET(0) NUMBITS(1) [],
72        EN1 OFFSET(1) NUMBITS(1) [],
73    ],
74    RESET_STATUS [
75        VAL0 OFFSET(0) NUMBITS(1) [],
76        VAL1 OFFSET(1) NUMBITS(1) [],
77    ],
78    WAKE_INFO_CAPTURE_DIS [
79        VAL OFFSET(0) NUMBITS(1) []
80    ],
81    WAKE_INFO [
82        REASONS OFFSET(0) NUMBITS(16) [],
83        FALL_THROUGH OFFSET(16) NUMBITS(1) [],
84        ABORT OFFSET(17) NUMBITS(1) []
85    ]
86];
87
88pub struct PwrMgr {
89    registers: StaticRef<PwrMgrRegisters>,
90}
91
92impl PwrMgr {
93    pub const fn new(base: StaticRef<PwrMgrRegisters>) -> PwrMgr {
94        PwrMgr { registers: base }
95    }
96
97    pub fn check_clock_propagation(&self) -> bool {
98        let regs = self.registers;
99
100        if regs.cfg_cdc_sync.read(CFG_CDC_SYNC::SYNC) == 0 {
101            return true;
102        }
103
104        false
105    }
106
107    pub fn handle_interrupt(&self) {
108        let regs = self.registers;
109
110        // Disable power saving
111        regs.control.write(CONTROL::LOW_POWER_HINT::CLEAR);
112
113        // Propagate changes to slow clock domain
114        regs.cfg_cdc_sync.write(CFG_CDC_SYNC::SYNC::SET);
115    }
116
117    pub fn enable_low_power(&self) {
118        let regs = self.registers;
119
120        if regs.control.read(CONTROL::LOW_POWER_HINT) != 1 {
121            // Next WFI should trigger low power entry
122            // Leave the IO clock enabled as we need to get interrupts
123            regs.control.write(
124                CONTROL::LOW_POWER_HINT::SET
125                    + CONTROL::CORE_CLK_EN::CLEAR
126                    + CONTROL::IO_CLK_EN::SET
127                    + CONTROL::MAIN_PD_N::CLEAR,
128            );
129
130            // Propagate changes to slow clock domain
131            regs.cfg_cdc_sync.write(CFG_CDC_SYNC::SYNC::SET);
132        }
133    }
134}