Skip to content

Commit 620a40a

Browse files
committed
Implement soc-manager-service
1 parent fcb7984 commit 620a40a

5 files changed

Lines changed: 338 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ members = [
2222
"debug-service",
2323
"debug-service-messages",
2424
"keyboard-service",
25+
"soc-manager-service",
2526
]
2627
exclude = ["examples/*"]
2728

soc-manager-service/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "soc-manager-service"
3+
version = "0.1.0"
4+
edition = "2024"
5+
description = "SoC manager embedded service implementation"
6+
repository = "https://github.com/OpenDevicePartnership/embedded-services"
7+
rust-version = "1.88"
8+
license = "MIT"
9+
10+
[lints]
11+
workspace = true
12+
13+
[dependencies]
14+
heapless = { workspace = true }
15+
defmt = { workspace = true, optional = true }
16+
embedded-services = { workspace = true }
17+
embassy-sync = { workspace = true }
18+
embedded-power-sequence = { git = "https://github.com/OpenDevicePartnership/embedded-power-sequence" }
19+
embedded-regulator = { git = "https://github.com/OpenDevicePartnership/embedded-regulator" }

soc-manager-service/src/lib.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
//! SoC manager service.
2+
#![no_std]
3+
4+
pub mod power_guard;
5+
6+
use embassy_sync::mutex::Mutex;
7+
use embassy_sync::watch::{Receiver, Watch};
8+
use embedded_power_sequence::PowerSequence;
9+
use embedded_services::GlobalRawMutex;
10+
11+
/// SoC manager service error.
12+
#[derive(Clone, Copy, Debug)]
13+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14+
pub enum Error {
15+
/// Unspecified error, likely some invariant was violated.
16+
Other,
17+
/// A power sequence error occurred.
18+
PowerSequence,
19+
/// An invalid power state transition was requested.
20+
InvalidStateTransition,
21+
/// No more power state listeners are available.
22+
ListenersNotAvailable,
23+
}
24+
25+
/// An ACPI power state.
26+
#[derive(Clone, Copy, Debug, PartialEq)]
27+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28+
pub enum PowerState {
29+
/// Working state.
30+
S0,
31+
/// Modern standby state.
32+
S0ix,
33+
/// Sleep state.
34+
S3,
35+
/// Hibernate state.
36+
S4,
37+
/// Soft off state.
38+
S5,
39+
}
40+
41+
/// A power state listener struct.
42+
pub struct PowerStateListener<'a, const MAX_LISTENERS: usize> {
43+
rx: Receiver<'a, GlobalRawMutex, PowerState, MAX_LISTENERS>,
44+
}
45+
46+
impl<'a, const MAX_LISTENERS: usize> PowerStateListener<'a, MAX_LISTENERS> {
47+
/// Waits for any power state change, returning the new power state.
48+
pub fn wait_state_change(&mut self) -> impl Future<Output = PowerState> {
49+
self.rx.changed()
50+
}
51+
52+
/// Waits for a transition to a specific power state.
53+
pub async fn wait_for_state(&mut self, power_state: PowerState) {
54+
self.rx.changed_and(|p| *p == power_state).await;
55+
}
56+
57+
/// Returns the current power state.
58+
///
59+
/// # Errors
60+
///
61+
/// Returns [`Error::Other`] if the power state is uninitialized.
62+
pub fn current_state(&mut self) -> Result<PowerState, Error> {
63+
self.rx.try_get().ok_or(Error::Other)
64+
}
65+
}
66+
67+
/// SoC manager.
68+
pub struct SocManager<T: PowerSequence, const MAX_LISTENERS: usize> {
69+
soc: Mutex<GlobalRawMutex, T>,
70+
power_state: Watch<GlobalRawMutex, PowerState, MAX_LISTENERS>,
71+
}
72+
73+
impl<T: PowerSequence, const MAX_LISTENERS: usize> SocManager<T, MAX_LISTENERS> {
74+
/// Creates a new SoC manager instance.
75+
///
76+
/// The `initial_state` should capture the power state the SoC is ALREADY in, not the desired state
77+
/// to transition to on initilization.
78+
///
79+
/// This will usually be [`PowerState::S5`] (powered off) but not always.
80+
pub fn new(soc: T, initial_state: PowerState) -> Self {
81+
let soc_manager = Self {
82+
soc: Mutex::new(soc),
83+
power_state: Watch::new(),
84+
};
85+
86+
soc_manager.power_state.sender().send(initial_state);
87+
soc_manager
88+
}
89+
90+
/// Creates a new power state listener.
91+
///
92+
/// # Errors
93+
///
94+
/// Returns [`Error::ListenersNotAvailable`] if `MAX_LISTENERS` or greater are already in use.
95+
pub fn new_pwr_listener(&self) -> Result<PowerStateListener<'_, MAX_LISTENERS>, Error> {
96+
Ok(PowerStateListener {
97+
rx: self.power_state.receiver().ok_or(Error::InvalidStateTransition)?,
98+
})
99+
}
100+
101+
/// Returns the current power state.
102+
///
103+
/// This method is also available on `PowerStateListener`.
104+
pub fn current_state(&self) -> Result<PowerState, Error> {
105+
self.power_state.try_get().ok_or(Error::Other)
106+
}
107+
108+
/// Sets the current power state.
109+
///
110+
/// # Errors
111+
///
112+
/// Returns [`Error::PowerSequence`] if an error is encountered while transitioning power state.
113+
///
114+
/// Returns [`Error::InvalidStateTransition`] if the requested state is not valid based on current state.
115+
pub async fn set_power_state(&self, state: PowerState) -> Result<(), Error> {
116+
// Revisit: Check with other services to see if we are too hot or don't have enough power for requested transition
117+
// Need to think more about how that will look though
118+
let cur_state = self.power_state.try_get().ok_or(Error::Other)?;
119+
let mut soc = self.soc.lock().await;
120+
match (cur_state, state) {
121+
// Any sleeping state must first transition to S0 before we can transition to another state
122+
(PowerState::S0ix, PowerState::S0) => soc.wake_up().await,
123+
(PowerState::S3, PowerState::S0) => soc.resume().await,
124+
(PowerState::S4, PowerState::S0) => soc.activate().await,
125+
(PowerState::S5, PowerState::S0) => soc.power_on().await,
126+
127+
// S0 can then transition to any sleep state
128+
(PowerState::S0, PowerState::S0ix) => soc.idle().await,
129+
(PowerState::S0, PowerState::S3) => soc.suspend().await,
130+
(PowerState::S0, PowerState::S4) => soc.hibernate().await,
131+
(PowerState::S0, PowerState::S5) => soc.power_off().await,
132+
133+
// Anything else is an invalid transition
134+
_ => return Err(Error::InvalidStateTransition),
135+
}
136+
.map_err(|_| Error::PowerSequence)?;
137+
138+
self.power_state.sender().send(state);
139+
Ok(())
140+
}
141+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//! PowerGuard.
2+
//!
3+
//! This is intended to be used within `embedded-power-sequence` implementations for handling
4+
//! rollback automatically while enabling/disabling power regulators.
5+
//!
6+
//! # Example
7+
//!
8+
//! ```
9+
//! impl<R: Regulator, I: InputPin + Wait> PowerSequence for SoC<R, I> {
10+
//! async fn power_on(&mut self) -> Result<(), Error> {
11+
//! let mut guard = power_guard::PowerGuard::<R, 3>::new();
12+
//!
13+
//! // If any of these fail, the PowerGuard will be implicitly rolled back
14+
//! guard.execute(power_guard::Op::Enable(&mut self.regulator1)).await?;
15+
//! guard.execute(power_guard::Op::Enable(&mut self.regulator2)).await?;
16+
//! guard.execute(power_guard::Op::Enable(&mut self.regulator3)).await?;
17+
//!
18+
//! // Typically at some point during sequencing we might wait for a "power good" pin to go high,
19+
//! // and if we timeout while waiting we can explicitly rollback the PowerGuard
20+
//! if with_timeout(Duration::from_millis(1000), self.pwr_good.wait_for_high()).await.is_err() {
21+
//! guard.rollback().await?;
22+
//! }
23+
//! }
24+
//! }
25+
//! ```
26+
use embedded_regulator::Regulator;
27+
use heapless::Vec;
28+
29+
/// PowerGuard error.
30+
#[derive(Clone, Copy, Debug)]
31+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
32+
pub enum Error {
33+
/// The PowerGuard is full and cannot accept more operations.
34+
Full,
35+
/// A regulator error occurred during rollback.
36+
RollbackFailure,
37+
/// A regulator error occurred while pushing an operation into the PowerGuard.
38+
OpFailure,
39+
/// The PowerGuard is empty.
40+
Empty,
41+
}
42+
43+
/// PowerGuard operation.
44+
#[derive(Debug)]
45+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
46+
pub enum Op<'a, R> {
47+
/// Enable regulator.
48+
Enable(&'a mut R),
49+
/// Disable regulator.
50+
Disable(&'a mut R),
51+
}
52+
53+
/// PowerGuard.
54+
///
55+
/// This represents a stack of operations on power regulators.
56+
/// As operations are pushed to the stack, they are executed.
57+
///
58+
/// In the event of an error, operations are undone and removed from the PowerGuard in reverse order.
59+
pub struct PowerGuard<'a, R: Regulator, const MAX_SIZE: usize> {
60+
stk: Vec<Op<'a, R>, MAX_SIZE>,
61+
}
62+
63+
impl<'a, R: Regulator, const MAX_SIZE: usize> Default for PowerGuard<'a, R, MAX_SIZE> {
64+
fn default() -> Self {
65+
Self { stk: Vec::new() }
66+
}
67+
}
68+
69+
impl<'a, R: Regulator, const MAX_SIZE: usize> PowerGuard<'a, R, MAX_SIZE> {
70+
/// Create a new PowerGuard instance.
71+
pub fn new() -> Self {
72+
Self::default()
73+
}
74+
75+
/// Rollback the PowerGuard. This will undo operations in reverse order of how they were entered.
76+
/// If successful, the PowerGuard will be empty upon return.
77+
///
78+
/// # Errors
79+
///
80+
/// Returns [`Error::RollbackFailure`] if a regulator error occurred during rollback.
81+
/// In this failure event, the PowerGuard may not be empty.
82+
pub async fn rollback(&mut self) -> Result<(), Error> {
83+
loop {
84+
match self.rollback_once().await {
85+
Ok(()) => continue,
86+
Err(Error::Empty) => return Ok(()),
87+
e @ Err(_) => return e,
88+
}
89+
}
90+
}
91+
92+
/// Rollback only the single most recent operation in the PowerGuard.
93+
///
94+
/// # Errors
95+
///
96+
/// Returns [`Error::Empty`] if the PowerGuard is empty.
97+
///
98+
/// Returns [`Error::RollbackFailure`] if a regulator error occurred during rollback.
99+
pub async fn rollback_once(&mut self) -> Result<(), Error> {
100+
match self.stk.pop() {
101+
Some(Op::Enable(r)) => r.disable().await,
102+
Some(Op::Disable(r)) => r.enable().await,
103+
None => return Err(Error::Empty),
104+
}
105+
.map_err(|_| Error::RollbackFailure)
106+
}
107+
108+
/// Execute an operation on a wrapped power regulator.
109+
/// If the operation fails, the PowerGuard will be automatically rolled back.
110+
///
111+
/// # Errors
112+
///
113+
/// Returns [`Error::Full`] if the PowerGuard is full. The PowerGuard is NOT rolled back in this case.
114+
///
115+
/// Returns [`Error::OpFailure`] if the operation failed but rollback was successful.
116+
///
117+
/// Returns [`Error::RollbackFailure`] if the operation failed and rollback failed as well.
118+
pub async fn execute(&mut self, mut cmd: Op<'a, R>) -> Result<(), Error> {
119+
if self.stk.is_full() {
120+
return Err(Error::Full);
121+
}
122+
123+
let res = match &mut cmd {
124+
Op::Enable(r) => r.enable().await,
125+
Op::Disable(r) => r.disable().await,
126+
};
127+
128+
if res.is_ok() {
129+
let _ = self.stk.push(cmd);
130+
Ok(())
131+
} else {
132+
self.rollback().await?;
133+
Err(Error::OpFailure)
134+
}
135+
}
136+
137+
/// Clears the PowerGuard of all operations.
138+
/// Thus, they will not be included in future rollbacks.
139+
pub fn clear(&mut self) {
140+
self.stk.clear();
141+
}
142+
}

0 commit comments

Comments
 (0)