-
Notifications
You must be signed in to change notification settings - Fork 226
WIP: PMBus Status Registers via MGS #2538
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
8720220
4c1fe97
41d352d
c806bcb
b13f7f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,22 +66,29 @@ macro_rules! pmbus_read { | |
| } | ||
|
|
||
| macro_rules! pmbus_rail_read { | ||
| ($device:expr, $rail:expr, $cmd:ident) => { | ||
| (@raw => $device:expr, $rail:expr, $cmd_code:expr, $len:expr) => { | ||
| $device | ||
| .write_read_reg::<u8, [u8; $cmd::CommandData::len()]>( | ||
| $cmd::CommandData::code(), | ||
| .write_read_reg::<u8, [u8; $len]>( | ||
| $cmd_code, | ||
| &[PAGE::CommandData::code(), $rail], | ||
| ) | ||
| .map_err(|code| Error::BadRead { | ||
| cmd: $cmd::CommandData::code(), | ||
| cmd: $cmd_code, | ||
| code, | ||
| }) | ||
| }; | ||
|
|
||
| ($device:expr, $rail:expr, $cmd:ident) => {{ | ||
| let cmd_code = $cmd::CommandData::code(); | ||
| const CMD_LEN: usize = $cmd::CommandData::len(); | ||
|
|
||
| pmbus_rail_read!(@raw => $device, $rail, cmd_code, CMD_LEN) | ||
| .and_then(|rval| { | ||
| $cmd::CommandData::from_slice(&rval).ok_or(Error::BadData { | ||
| cmd: $cmd::CommandData::code(), | ||
| }) | ||
| }) | ||
| }; | ||
| }}; | ||
|
|
||
| ($device:expr, $rail:expr, $dev:ident::$cmd:ident) => {{ | ||
| use $dev::{PAGE, $cmd}; | ||
|
|
@@ -271,6 +278,52 @@ pub trait Validate<T: core::convert::Into<drv_i2c_api::ResponseCode>> { | |
| } | ||
| } | ||
|
|
||
| // grumble grumble, copied from `gateway_messages::sp_to_mgs::PmbusStatus` | ||
| // grumble grumble, also basically the same as `ereports/src/pwr` | ||
| pub struct PmbusStatus { | ||
| pub status_word: u16, | ||
| pub status_vout: u8, | ||
| pub status_iout: u8, | ||
| pub status_temperature: u8, | ||
| pub status_cml: u8, | ||
| pub status_other: u8, | ||
| pub status_input: u8, | ||
| pub status_mfr_specific: u8, | ||
| pub status_fans_1_2: u8, | ||
| pub status_fans_3_4: u8, | ||
| } | ||
|
|
||
| pub enum PmbusStatusError { | ||
| BadRead { cmd: u8, code: ResponseCode }, | ||
| BadData { cmd: u8, }, | ||
| } | ||
|
|
||
| impl PmbusStatus { | ||
| pub fn read_from(dev: &I2cDevice, rail_idx: u8) -> Result<Self, ()> { | ||
| use pmbus::commands::*; | ||
| use PmbusStatusError as Error; | ||
|
|
||
| Ok(PmbusStatus { | ||
| status_word: pmbus_rail_read!(dev, rail_idx, STATUS_WORD).map_err(drop)?.0, | ||
| status_vout: pmbus_rail_read!(dev, rail_idx, STATUS_VOUT).map_err(drop)?.0, | ||
| status_iout: pmbus_rail_read!(dev, rail_idx, STATUS_IOUT).map_err(drop)?.0, | ||
| status_temperature: pmbus_rail_read!(dev, rail_idx, STATUS_TEMPERATURE).map_err(drop)?.0, | ||
| status_cml: pmbus_rail_read!(dev, rail_idx, STATUS_CML).map_err(drop)?.0, | ||
| status_other: pmbus_rail_read!(dev, rail_idx, STATUS_OTHER).map_err(drop)?.0, | ||
| status_input: pmbus_rail_read!(dev, rail_idx, STATUS_INPUT).map_err(drop)?.0, | ||
| status_fans_1_2: pmbus_rail_read!(dev, rail_idx, STATUS_FANS_1_2).map_err(drop)?.0, | ||
| status_fans_3_4: pmbus_rail_read!(dev, rail_idx, STATUS_FANS_3_4).map_err(drop)?.0, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm...I'd be interested to know what happens if we try to read either §17.10 and §17.10 in the PMBus Specification, Part II, Version 1.3.1 doesn't explicitly say what a device without fans is supposed to do, so, if I had to guess, it's probably left up to the manufacturer...
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Attempting to find out what happens when one does this experimentally, and I have discovered that, using the eliza@alfred ~ $ pfexec /staff/eliza/humility -t cosmo-hubris-sp pmbus -r VDDCR_CPU0_A0 -C STATUS_FANS_1_2
humility: attached to 1fc9:0143:XJACFXSEKTQJS via CMSIS-DAP
eliza@alfred ~ $I'm not actually sure what "nothing" means here!
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The verbose mode also makes it say "nothing": eliza@alfred ~ $ pfexec /staff/eliza/humility -t cosmo-hubris-sp pmbus -r VDDCR_CPU0_A0 -C STATUS_FANS_1_2 --verbose
humility: attached to 1fc9:0143:XJACFXSEKTQJS via CMSIS-DAP
eliza@alfred ~ $
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OH, you have to pass the eliza@alfred ~ $ pfexec /staff/eliza/humility -t cosmo-hubris-sp pmbus -r VDDCR_CPU0_A0 -C STATUS_FANS_1_2,STATUS_FANS_3_4 --verbose --errors
humility: attached to 1fc9:0143:XJACFXSEKTQJS via CMSIS-DAP
0x81 STATUS_FANS_1_2 Err(NoRegister)
0x82 STATUS_FANS_3_4 Err(NoRegister)
eliza@alfred ~ $The ISL68224, ADM127X, TPS546B24A also do the same when asked for their eliza@alfred ~ $ pfexec /staff/eliza/humility -t cosmo-hubris-sp pmbus -r VDDCR_CPU0_A0 -C STATUS_FANS_1_2 --verbose --errors
humility: attached to 1fc9:0143:XJACFXSEKTQJS via CMSIS-DAP
0x81 STATUS_FANS_1_2 Err(NoRegister)
eliza@alfred ~ $ pfexec /staff/eliza/humility -t cosmo-hubris-sp pmbus -r VDDCR_CPU0_A0 -C STATUS_FANS_1_2,STATUS_FANS_3_4 --verbose --errors
humility: attached to 1fc9:0143:XJACFXSEKTQJS via CMSIS-DAP
0x81 STATUS_FANS_1_2 Err(NoRegister)
0x82 STATUS_FANS_3_4 Err(NoRegister)
eliza@alfred ~ $ pfexec /staff/eliza/humility -t cosmo-hubris-sp pmbus -r V1P8_SP5_A0 -C STATUS_FANS_1_2,STATUS_FANS_3_4 --verbose --errors
humility: attached to 1fc9:0143:XJACFXSEKTQJS via CMSIS-DAP
humility pmbus failed: rail V1P8_SP5_A0 not found
eliza@alfred ~ $ pfexec /staff/eliza/humility -t cosmo-hubris-sp pmbus -r V1P8_SP5_A1 -C STATUS_FANS_1_2,STATUS_FANS_3_4 --verbose --errors
humility: attached to 1fc9:0143:XJACFXSEKTQJS via CMSIS-DAP
0x81 STATUS_FANS_1_2 Err(NoRegister)
0x82 STATUS_FANS_3_4 Err(NoRegister)
eliza@alfred ~ $ pfexec /staff/eliza/humility -t cosmo-hubris-sp pmbus -r V54P5_IBC_A3 -C STATUS_FANS_1_2,STATUS_FANS_3_4 --verbose --errors
humility: attached to 1fc9:0143:XJACFXSEKTQJS via CMSIS-DAP
0x81 STATUS_FANS_1_2 Err(NoRegister)
0x82 STATUS_FANS_3_4 Err(NoRegister)
eliza@alfred ~ $ pfexec /staff/eliza/humility -t cosmo-hubris-sp pmbus -r V3P3_SP_A2 -C STATUS_FANS_1_2,STATUS_FANS_3_4 --verbose --errors
humility: attached to 1fc9:0143:XJACFXSEKTQJS via CMSIS-DAP
0x81 STATUS_FANS_1_2 Err(NoRegister)
0x82 STATUS_FANS_3_4 Err(NoRegister)
eliza@alfred ~ $ pfexec /staff/eliza/humility -t cosmo-hubris-sp pmbus -r V12_SYS_A2 -C STATUS_FANS_1_2,STATUS_FANS_3_4 --verbose --errors
humility: attached to 1fc9:0143:XJACFXSEKTQJS via CMSIS-DAP
eliza@alfred ~ $This is sort of what I had figured, and suggests that we need to either have some additional configuration option that says whether or not to read the fans, and/or (and this would be my preference) change |
||
|
|
||
| // Unfortunately, STATUS_MFR_SPECIFIC *is* defined in the pmbus crate, but doesn't have a | ||
| // "structured" representation, so instead use a raw representation. It *could* be argued | ||
| // that since we're not actually peeking at any of the introspection stuff, we could do | ||
| // the same for all the items above, and save ourselved a little indirection, but for now | ||
| // just hole-punch the minimum amount necessary | ||
| status_mfr_specific: pmbus_rail_read!(@raw => dev, rail_idx, CommandCode::STATUS_MFR_SPECIFIC as u8, 1).map_err(drop)?[0], | ||
| }) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't love that this will bail out if any read returns an error. I think it would be much better to change the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do! I wondered whether to make these I wonder if we could capture "this device supports these statuses" in the Since we're not using device-specific methods here, I would imagine we would want to pass in some kind of bit-flag of which ones to check or not, and just have a
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, we could either do this in the I do worry slightly about a device firmware revision changing its behavior to something that doesn't match the codegen's understanding, but that feels pretty unlikely. For instance, an RAA229620A VRM is not going to suddenly grow fans if its firmware is updated! Incidentally, there is another option, which is the PMBus At the end of the day, I just don't really think that's particularly worthwhile: since we know all the devices on the board at build time, we can just decide which registers to read when we do the code generation, and we don't have to dynamically detect capabilities. I think that |
||
| } | ||
| } | ||
|
|
||
| pub mod adm127x; | ||
| pub mod adt7420; | ||
| pub mod at24csw080; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,6 +5,11 @@ | |||||||||||||||||||||||||||||||||||||||||
| use std::io::Write; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { | ||||||||||||||||||||||||||||||||||||||||||
| if let Err(e) = build_i2c::codegen(build_i2c::Disposition::Devices) { | ||||||||||||||||||||||||||||||||||||||||||
| println!("cargo::error=failed to generate I2C devices: {e}"); | ||||||||||||||||||||||||||||||||||||||||||
| std::process::exit(1); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Soooo, I realize that in my VPD PR, I added all the code generation to Since we're looking up devices for reading PMBus status using a new lookup table of rail names to devices, I think there isn't actually any reason that that needs to go in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that sounds reasonable! I put it in I'll move it over to control plane agent, or as a new codegen path. |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| write_pub_device_descriptions()?; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| idol::client::build_client_stub( | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -21,7 +26,7 @@ fn write_pub_device_descriptions() -> anyhow::Result<()> { | |||||||||||||||||||||||||||||||||||||||||
| let out_dir = std::env::var("OUT_DIR")?; | ||||||||||||||||||||||||||||||||||||||||||
| let dest_path = | ||||||||||||||||||||||||||||||||||||||||||
| std::path::Path::new(&out_dir).join("device_descriptions.rs"); | ||||||||||||||||||||||||||||||||||||||||||
| let file = std::fs::File::create(dest_path)?; | ||||||||||||||||||||||||||||||||||||||||||
| let file = std::fs::File::create(&dest_path)?; | ||||||||||||||||||||||||||||||||||||||||||
| let mut file = std::io::BufWriter::new(file); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| writeln!( | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -57,13 +62,14 @@ fn write_pub_device_descriptions() -> anyhow::Result<()> { | |||||||||||||||||||||||||||||||||||||||||
| // key. | ||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||
| let mut id2idx = std::collections::BTreeMap::new(); | ||||||||||||||||||||||||||||||||||||||||||
| let mut pmbus_rail_names = std::collections::BTreeSet::new(); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| for (idx, dev) in devices.into_iter().enumerate() { | ||||||||||||||||||||||||||||||||||||||||||
| for (idx, dev) in devices.iter().cloned().enumerate() { | ||||||||||||||||||||||||||||||||||||||||||
| let is_pmbus = dev.is_pmbus(); | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file, " DeviceDescription {{")?; | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file, " device: {:?},", dev.device)?; | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file, " description: {:?},", dev.description)?; | ||||||||||||||||||||||||||||||||||||||||||
| if let Some(id) = dev.device_id { | ||||||||||||||||||||||||||||||||||||||||||
| if let Some(ref id) = dev.device_id { | ||||||||||||||||||||||||||||||||||||||||||
| if let Ok(component) = SpComponent::try_from(id.as_ref()) { | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file, " id: {:?},", component.id)?; | ||||||||||||||||||||||||||||||||||||||||||
| if id2idx.insert(component.id, idx).is_some() { | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -77,6 +83,15 @@ fn write_pub_device_descriptions() -> anyhow::Result<()> { | |||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||
| ids_too_long += 1; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if let Some(ref pmbus) = dev.pmbus { | ||||||||||||||||||||||||||||||||||||||||||
| for rail in pmbus.rails.iter() { | ||||||||||||||||||||||||||||||||||||||||||
| // Returns "is unique", unlike `BTreeMap::insert().is_some()`! | ||||||||||||||||||||||||||||||||||||||||||
| if !pmbus_rail_names.insert(rail.name.clone()) { | ||||||||||||||||||||||||||||||||||||||||||
| panic!("cargo::warn=dupe: {:?}, {:?}", rail.name, dev); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||
| println!( | ||||||||||||||||||||||||||||||||||||||||||
| "cargo::error=device {:?} ({:?}) hath no device ID (refdes)", | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -114,6 +129,39 @@ fn write_pub_device_descriptions() -> anyhow::Result<()> { | |||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file, "];")?; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| let max_len = pmbus_rail_names.iter().map(String::len).max(); | ||||||||||||||||||||||||||||||||||||||||||
| if let Some(_len) = max_len { | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // TODO: do we need fixed-length bstrings? If we want to binary search, we probably want them to | ||||||||||||||||||||||||||||||||||||||||||
| // be truncated to some maximum length, so we either need to define the max length at the | ||||||||||||||||||||||||||||||||||||||||||
| // protocol level so mgs knows how long of a string to send us, or we could instead trim the | ||||||||||||||||||||||||||||||||||||||||||
| // trailing nulls and search by that instead. | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i suspect the lookup code is going to end up looking something like the code in hubris/task/control-plane-agent/src/inventory.rs Lines 177 to 196 in 96975a6
as for maximum length, i think that the wire message will certainly have to have one, a la I really don't love the null-padding, but even if we were to adopt an encoding that let us not do that part, we would still need to have some max length somewhere so we can limit the buffer size...
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think the distinction that I wanted to make (or at least still need to shake out) is that just because it needs to be zero-padded on the wire, that doesn't necessarily mean that internally we ALSO need to zero-pad it. When receiving the command over the wire, we could first trim any trailing nulls, and then use the slice locally instead. That being said, if I bump into anywhere else where we need fixed length arrays, I'll probably revert back to zero-padding in the codegen.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think avoiding zero-padding here is probably nicer if you can manage it --- it should save us a bit of flash in the lookup table even if it doesn't save us the stack space when we're passing around the wire message. |
||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||
| // writeln!(file, "pub const MAX_PMBUS_RAIL_NAME: usize = {len};")?; | ||||||||||||||||||||||||||||||||||||||||||
| // writeln!(file, "pub const PMBUS_RAIL_TO_I2C_DEVICE_MAP: [([u8; MAX_PMBUS_RAIL_NAME], fn(TaskId) -> (drv_i2c_api::I2cDevice, u8)); {}] = [", pmbus_rail_names.len())?; | ||||||||||||||||||||||||||||||||||||||||||
| // for rail in pmbus_rail_names.iter() { | ||||||||||||||||||||||||||||||||||||||||||
| // write!(file, " (*b\"{rail}")?; | ||||||||||||||||||||||||||||||||||||||||||
| // for _ in 0..(len.checked_sub(rail.len()).unwrap()) { | ||||||||||||||||||||||||||||||||||||||||||
| // write!(file, "\\0")?; | ||||||||||||||||||||||||||||||||||||||||||
| // } | ||||||||||||||||||||||||||||||||||||||||||
| // write!(file, "\", ")?; | ||||||||||||||||||||||||||||||||||||||||||
| // write!(file, "crate::i2c_config::pmbus::{}", rail.to_lowercase())?; | ||||||||||||||||||||||||||||||||||||||||||
| // writeln!(file, "),")?; | ||||||||||||||||||||||||||||||||||||||||||
| // } | ||||||||||||||||||||||||||||||||||||||||||
| // writeln!(file, "];")?; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // Assuming we are going the trimmed route... | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file)?; | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file, "pub const PMBUS_RAIL_TO_I2C_DEVICE_MAP: [(&[u8], fn(TaskId) -> (drv_i2c_api::I2cDevice, u8)); {}] = [", pmbus_rail_names.len())?; | ||||||||||||||||||||||||||||||||||||||||||
| for rail in pmbus_rail_names.iter() { | ||||||||||||||||||||||||||||||||||||||||||
| write!(file, " (b\"{rail}\", ")?; | ||||||||||||||||||||||||||||||||||||||||||
| // build_i2c *also* only to-lowercases the rail names to make functions | ||||||||||||||||||||||||||||||||||||||||||
| write!(file, "crate::i2c_config::pmbus::{}", rail.to_lowercase())?; | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file, "),")?; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file, "];")?; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| file.flush()?; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| anyhow::ensure!(missing_ids == 0, "{missing_ids} devices have no ID!"); | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -129,5 +177,9 @@ fn write_pub_device_descriptions() -> anyhow::Result<()> { | |||||||||||||||||||||||||||||||||||||||||
| SpComponent::MAX_ID_LENGTH, | ||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // panic!("{}", dest_path.display()); | ||||||||||||||||||||||||||||||||||||||||||
| // panic!("{max_len:?} {:#?}", pmbus_rail_names); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Ok(()) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this is now in the
control-plane-agentbuild script...