Skip to content

Commit 4b1c4d4

Browse files
committed
Add command to get and set charge limit
The minimum limit cannot be changed. Tested on platforms: - Intel 12th Gen with BIOS 3.07 - AMD 13th inch with BIOS 3.04 Tested with drivers; - portio on UEFI Shell - portio on Linux - cros_ec on Linux Examples: ``` > framework_tool --driver portio --charge-limit Minimum 0%, Maximum 100% > framework_tool --charge-limit 90 Minimum 0%, Maximum 90% ``` Signed-off-by: Daniel Schaefer <[email protected]>
1 parent c157d53 commit 4b1c4d4

File tree

6 files changed

+111
-0
lines changed

6 files changed

+111
-0
lines changed

framework_lib/src/chromium_ec/command.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub enum EcCommands {
3030
// Framework specific commands
3131
/// Configure the behavior of the flash notify
3232
FlashNotified = 0x3E01,
33+
/// Change charge limit
34+
ChargeLimitControl = 0x3E03,
3335
/// Get information about the current chassis open/close status
3436
ChassisOpenCheck = 0x3E0F,
3537
/// Get information about historical chassis open/close (intrusion) information

framework_lib/src/chromium_ec/commands.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,3 +359,41 @@ impl EcRequest<EcResponseGetHwDiag> for EcRequestGetHwDiag {
359359
EcCommands::GetHwDiag
360360
}
361361
}
362+
363+
#[repr(u8)]
364+
pub enum ChargeLimitControlModes {
365+
/// Disable all settings, handled automatically
366+
Disable = 0x01,
367+
/// Set maxiumum and minimum percentage
368+
Set = 0x02,
369+
/// Get current setting
370+
/// ATTENTION!!! This is the only mode that will return a response
371+
Get = 0x08,
372+
/// Allow charge to full this time
373+
Override = 0x80,
374+
}
375+
376+
#[repr(C, packed)]
377+
pub struct EcRequestChargeLimitControl {
378+
pub modes: u8,
379+
pub max_percentage: u8,
380+
pub min_percentage: u8,
381+
}
382+
383+
#[repr(C, packed)]
384+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
385+
pub struct EcResponseChargeLimitControl {
386+
pub max_percentage: u8,
387+
pub min_percentage: u8,
388+
}
389+
390+
impl EcRequest<EcResponseChargeLimitControl> for EcRequestChargeLimitControl {
391+
fn command_id() -> EcCommands {
392+
EcCommands::ChargeLimitControl
393+
}
394+
}
395+
396+
/*
397+
* Configure the behavior of the charge limit control.
398+
*/
399+
pub const EC_CHARGE_LIMIT_RESTORE: u8 = 0x7F;

framework_lib/src/chromium_ec/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,33 @@ impl CrosEc {
229229
Ok((status.microphone == 1, status.camera == 1))
230230
}
231231

232+
pub fn set_charge_limit(&self, min: u8, max: u8) -> EcResult<()> {
233+
// Sending bytes manually because the Set command, as opposed to the Get command,
234+
// does not return any data
235+
let limits = &[ChargeLimitControlModes::Set as u8, max, min];
236+
let data = self.send_command(EcCommands::ChargeLimitControl as u16, 0, limits)?;
237+
assert_eq!(data.len(), 0);
238+
239+
Ok(())
240+
}
241+
242+
/// Get charge limit in percent (min, max)
243+
pub fn get_charge_limit(&self) -> EcResult<(u8, u8)> {
244+
let limits = EcRequestChargeLimitControl {
245+
modes: ChargeLimitControlModes::Get as u8,
246+
max_percentage: 0xFF,
247+
min_percentage: 0xFF,
248+
}
249+
.send_command(self)?;
250+
251+
debug!(
252+
"Min Raw: {}, Max Raw: {}",
253+
limits.min_percentage, limits.max_percentage
254+
);
255+
256+
Ok((limits.min_percentage, limits.max_percentage))
257+
}
258+
232259
/// Get the intrusion switch status (whether the chassis is open or not)
233260
pub fn get_intrusion_status(&self) -> EcResult<IntrusionStatus> {
234261
let status = EcRequestChassisOpenCheck {}.send_command(self)?;

framework_lib/src/commandline/clap_std.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ struct ClapCli {
8989
#[arg(long)]
9090
input_deck_mode: Option<InputDeckModeArg>,
9191

92+
/// Get or set max charge limit
93+
#[arg(long)]
94+
charge_limit: Option<Option<u8>>,
95+
9296
/// Set keyboard backlight percentage or get, if no value provided
9397
#[arg(long)]
9498
kblight: Option<Option<u8>>,
@@ -142,6 +146,7 @@ pub fn parse(args: &[String]) -> Cli {
142146
intrusion: args.intrusion,
143147
inputmodules: args.inputmodules,
144148
input_deck_mode: args.input_deck_mode,
149+
charge_limit: args.charge_limit,
145150
kblight: args.kblight,
146151
console: args.console,
147152
driver: args.driver,

framework_lib/src/commandline/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ use crate::ccgx::{self, SiliconId::*};
3030
use crate::chromium_ec;
3131
use crate::chromium_ec::commands::DeckStateMode;
3232
use crate::chromium_ec::print_err;
33+
use crate::chromium_ec::EcError;
34+
use crate::chromium_ec::EcResult;
3335
#[cfg(feature = "linux")]
3436
use crate::csme;
3537
use crate::ec_binary;
@@ -100,6 +102,7 @@ pub struct Cli {
100102
pub intrusion: bool,
101103
pub inputmodules: bool,
102104
pub input_deck_mode: Option<InputDeckModeArg>,
105+
pub charge_limit: Option<Option<u8>>,
103106
pub kblight: Option<Option<u8>>,
104107
pub console: Option<ConsoleArg>,
105108
pub help: bool,
@@ -435,6 +438,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
435438
} else if let Some(mode) = &args.input_deck_mode {
436439
println!("Set mode to: {:?}", mode);
437440
ec.set_input_deck_mode((*mode).into()).unwrap();
441+
} else if let Some(maybe_limit) = args.charge_limit {
442+
print_err(handle_charge_limit(&ec, maybe_limit));
438443
} else if let Some(Some(kblight)) = args.kblight {
439444
assert!(kblight <= 100);
440445
ec.set_keyboard_backlight(kblight);
@@ -841,3 +846,21 @@ pub fn analyze_capsule(data: &[u8]) -> Option<capsule::EfiCapsuleHeader> {
841846

842847
Some(header)
843848
}
849+
850+
fn handle_charge_limit(ec: &CrosEc, maybe_limit: Option<u8>) -> EcResult<()> {
851+
let (cur_min, _cur_max) = ec.get_charge_limit()?;
852+
if let Some(limit) = maybe_limit {
853+
// Prevent accidentally setting a very low limit
854+
if limit < 25 {
855+
return Err(EcError::DeviceError(
856+
"Not recommended to set charge limit below 25%".to_string(),
857+
));
858+
}
859+
ec.set_charge_limit(cur_min, limit)?;
860+
}
861+
862+
let (min, max) = ec.get_charge_limit()?;
863+
println!("Minimum {}%, Maximum {}%", min, max);
864+
865+
Ok(())
866+
}

framework_lib/src/commandline/uefi.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub fn parse(args: &[String]) -> Cli {
7373
intrusion: false,
7474
inputmodules: false,
7575
input_deck_mode: None,
76+
charge_limit: None,
7677
kblight: None,
7778
console: None,
7879
// This is the only driver that works on UEFI
@@ -151,6 +152,21 @@ pub fn parse(args: &[String]) -> Cli {
151152
None
152153
};
153154
found_an_option = true;
155+
} else if arg == "--charge-limit" {
156+
cli.charge_limit = if args.len() > i + 1 {
157+
if let Ok(percent) = args[i + 1].parse::<u8>() {
158+
Some(Some(percent))
159+
} else {
160+
println!(
161+
"Invalid value for --charge_limit: '{}'. Must be integer < 100.",
162+
args[i + 1]
163+
);
164+
None
165+
}
166+
} else {
167+
Some(None)
168+
};
169+
found_an_option = true;
154170
} else if arg == "--kblight" {
155171
cli.kblight = if args.len() > i + 1 {
156172
if let Ok(percent) = args[i + 1].parse::<u8>() {

0 commit comments

Comments
 (0)