-
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 4 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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,24 +66,31 @@ 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) => {{ | ||
| ($device:expr, $rail:expr, $dev:ident::$cmd:ident $(,)?) => {{ | ||
| use $dev::{PAGE, $cmd}; | ||
| pmbus_rail_read!($device, $rail, $cmd) | ||
| }}; | ||
|
|
@@ -271,6 +278,69 @@ 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: Result<u8, PmbusStatusError>, | ||
| pub status_iout: Result<u8, PmbusStatusError>, | ||
| pub status_temperature: Result<u8, PmbusStatusError>, | ||
| pub status_cml: Result<u8, PmbusStatusError>, | ||
| pub status_other: Result<u8, PmbusStatusError>, | ||
| pub status_input: Result<u8, PmbusStatusError>, | ||
| pub status_mfr_specific: Result<u8, PmbusStatusError>, | ||
| pub status_fans_1_2: Result<u8, PmbusStatusError>, | ||
| pub status_fans_3_4: Result<u8, PmbusStatusError>, | ||
| } | ||
|
|
||
| /// An error for querying PMBus `STATUS_*` registers. | ||
| #[derive(Copy, Clone, Debug, PartialEq)] | ||
| pub enum PmbusStatusError { | ||
| BadRead { cmd: u8, code: ResponseCode }, | ||
| BadData { cmd: u8, }, | ||
| } | ||
|
|
||
| impl PmbusStatus { | ||
| /// Attempt to read a [`PmbusStatus`] from the given device and rail. | ||
| /// | ||
| /// This function returns an error if the initial attempt to obtain `STATUS_WORD` from the | ||
| /// device fails, otherwise returnining successfully even if "leaf" status bytes were unable | ||
| /// to be read, either due to ephemeral hiccups, or that status byte being unsupported by | ||
| /// the device queried. | ||
|
Comment on lines
+306
to
+309
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'm not entirely convinced this behavior for That said, I think it's also reasonable-ish to fail fast if we get an error that indicates there is no device to talk to at all; if we get a |
||
| pub fn try_read_from(dev: &I2cDevice, rail_idx: u8) -> Result<Self, PmbusStatusError> { | ||
| use pmbus::commands::*; | ||
| use PmbusStatusError as Error; | ||
|
|
||
| Ok(PmbusStatus { | ||
| // Status word *must* succeed, otherwise we don't have reasonable data to return. | ||
| // We may want to consider making some/all of these retryable, but for now you either | ||
| // get them or you don't. | ||
| status_word: pmbus_rail_read!(dev, rail_idx, STATUS_WORD)?.0, | ||
| status_vout: pmbus_rail_read!(dev, rail_idx, STATUS_VOUT).map(|v| v.0), | ||
| status_iout: pmbus_rail_read!(dev, rail_idx, STATUS_IOUT).map(|v| v.0), | ||
| status_temperature: pmbus_rail_read!(dev, rail_idx, STATUS_TEMPERATURE).map(|v| v.0), | ||
| status_cml: pmbus_rail_read!(dev, rail_idx, STATUS_CML).map(|v| v.0), | ||
| status_other: pmbus_rail_read!(dev, rail_idx, STATUS_OTHER).map(|v| v.0), | ||
| status_input: pmbus_rail_read!(dev, rail_idx, STATUS_INPUT).map(|v| v.0), | ||
| status_fans_1_2: pmbus_rail_read!(dev, rail_idx, STATUS_FANS_1_2).map(|v| v.0), | ||
| status_fans_3_4: pmbus_rail_read!(dev, rail_idx, STATUS_FANS_3_4).map(|v| v.0), | ||
|
|
||
| // Unfortunately, STATUS_MFR_SPECIFIC *is* defined in the pmbus crate, but doesn't have a | ||
| // "structured" representation, so instead use a raw accessor. 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 ourselves a little indirection, but for now | ||
| // just hole-punch the minimum amount necessary. Indexing is fine here because we know | ||
| // (statically) the returned value is `[u8; 1]`. | ||
| status_mfr_specific: pmbus_rail_read!( | ||
| @raw => dev, | ||
| rail_idx, | ||
| CommandCode::STATUS_MFR_SPECIFIC as u8, | ||
| 1, | ||
| ).map(|v| v[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 | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -35,6 +35,64 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { | |||||||||||||||||||||||||||||||||||||||||
| if let Some(cfg) = cfg { | ||||||||||||||||||||||||||||||||||||||||||
| write_keys(cfg)?; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| do_pmbus()?; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Ok(()) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fn do_pmbus() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { | ||||||||||||||||||||||||||||||||||||||||||
| let out_dir = std::env::var("OUT_DIR")?; | ||||||||||||||||||||||||||||||||||||||||||
| let dest_path = | ||||||||||||||||||||||||||||||||||||||||||
| std::path::Path::new(&out_dir).join("pmbus_mapping.rs"); | ||||||||||||||||||||||||||||||||||||||||||
|
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. unimportant turbo-nit:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
| let file = std::fs::File::create(&dest_path)?; | ||||||||||||||||||||||||||||||||||||||||||
| let mut file = std::io::BufWriter::new(file); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // Generate the necessary rail names | ||||||||||||||||||||||||||||||||||||||||||
| if let Err(e) = build_i2c::codegen(build_i2c::Disposition::Devices) { | ||||||||||||||||||||||||||||||||||||||||||
| println!("cargo::error=failed to generate I2C devices: {e}"); | ||||||||||||||||||||||||||||||||||||||||||
| std::process::exit(1); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+51
to
+55
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. nitpicky: i might put this part in |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| let devices = build_i2c::device_descriptions() | ||||||||||||||||||||||||||||||||||||||||||
| .collect::<Vec<_>>(); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| let mut pmbus_rail_names = std::collections::BTreeSet::new(); | ||||||||||||||||||||||||||||||||||||||||||
| let mut pmbus_rail_dupes = 0; | ||||||||||||||||||||||||||||||||||||||||||
| for dev in devices { | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+57
to
+62
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. nit: since all we do with
Suggested change
or even just:
Suggested change
i doubt the build time hit for iterating twice and allocating here is all that noticeable, but...every nanosecond counts, i guess? |
||||||||||||||||||||||||||||||||||||||||||
| // We only need to map PMBus devices | ||||||||||||||||||||||||||||||||||||||||||
| let Some(ref pmbus) = dev.pmbus else { | ||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // pmbus devices must have a refdes | ||||||||||||||||||||||||||||||||||||||||||
| assert!(dev.device_id.is_some()); | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+68
to
+69
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. do we actually care about this here? |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // Aggregate a list of all PMBus-visible rails | ||||||||||||||||||||||||||||||||||||||||||
| for rail in pmbus.rails.iter() { | ||||||||||||||||||||||||||||||||||||||||||
| // `BTreeSet::insert` return value means "is unique", which is the inverse of | ||||||||||||||||||||||||||||||||||||||||||
| // `BTreeMap::insert().is_some()`! | ||||||||||||||||||||||||||||||||||||||||||
| if !pmbus_rail_names.insert(rail.name.clone()) { | ||||||||||||||||||||||||||||||||||||||||||
| pmbus_rail_dupes += 1; | ||||||||||||||||||||||||||||||||||||||||||
| println!("cargo::error=Duplicate Rail name: {:?}", rail.name); | ||||||||||||||||||||||||||||||||||||||||||
|
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. nit: this error message is what the programmer will see if they have made an error in the
Suggested change
or something like that? |
||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // Create a mapping between rail names and generated accessor functions for obtaining | ||||||||||||||||||||||||||||||||||||||||||
| // the device handle and rail index | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file)?; | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file, "pub const PMBUS_RAIL_TO_I2C_DEVICE_MAP: [(&[u8], fn(userlib::TaskId) -> (drv_i2c_api::I2cDevice, u8)); {}] = [", pmbus_rail_names.len())?; | ||||||||||||||||||||||||||||||||||||||||||
|
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. nit, take it or leave it: part of me wants to suggest that this be a struct rather than a tuple, in the hopes that the non-generated code consuming the LUT is a bit more readable. but i'm not sure if this is worth the ffort or not. up to you. |
||||||||||||||||||||||||||||||||||||||||||
| 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())?; | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+88
to
+89
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. eventually, if we wanted to be fancy, we could probably change up could be worth adding to the backlog of "non-important non-blockers"... |
||||||||||||||||||||||||||||||||||||||||||
| writeln!(file, "),")?; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| writeln!(file, "];")?; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(pmbus_rail_dupes, 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. nitpick: it would be nice if this had a panic message that said something to the effect of "your config is invalid"...also, it's maybe worth checking whether panicking or returning an |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| 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...