observer: MWOCP67 PMBus Support#2549
Conversation
| # This one device actually appears at 8 different i2c addresses. 0b1010_000 - | ||
| # 0b1010_011 let you access the main memory registers, where the 2 LSBs of the | ||
| # device address are the 2 MSBs of the memory address. 0b1011_000 - 0b1011_011 | ||
| # let you access the security registers and write protection registers. |
There was a problem hiding this comment.
FWIW --- and I am not saying that we should get rid of this comment, for the record --- this is an EEPROM we use kinda all over the place (there's one on pretty much every board), so perhaps we should document this in a more centralized location.
There was a problem hiding this comment.
Any thoughts about where it should go? This info is in the datasheet, I just added a note here because I was confused about why I saw 8 devices on the bus.
| const REVISION_LEN: usize = 14; | ||
|
|
||
| let mut data = [0u8; REVISION_LEN]; | ||
| let expected = b"XXXX-YYYY-0000"; | ||
|
|
||
| let len = self | ||
| .device | ||
| .read_block(CommandCode::MFR_REVISION as u8, &mut data) | ||
| .map_err(|code| Error::BadFirmwareRevRead { code })?; | ||
|
|
||
| // | ||
| // Per ACAN-157, we are expecting this to be of the format: | ||
| // | ||
| // XXXX-YYYY-0000 | ||
| // | ||
| // Where XXXX is the firmware revision on the primary MCU (AC input | ||
| // side) and YYYY is the firmware revision on the secondary MCU (DC | ||
| // output side). We aren't going to be rigid about the format of | ||
| // either revision, but we will be rigid about the rest of the format. | ||
| // | ||
| if len != REVISION_LEN { | ||
| return Err(Error::BadFirmwareRevLength); | ||
| } | ||
|
|
||
| for index in 0..len { | ||
| if expected[index] == b'X' || expected[index] == b'Y' { | ||
| continue; | ||
| } | ||
|
|
||
| if data[index] != expected[index] { | ||
| return Err(Error::BadFirmwareRev { index: index as u8 }); | ||
| } | ||
| } | ||
|
|
||
| // | ||
| // Return the primary MCU version | ||
| // | ||
| Ok(FirmwareRev([data[0], data[1], data[2], data[3]])) |
There was a problem hiding this comment.
This looks like the same format as the one used by the MWOCP68, right? That observation kinda makes me want to push for pulling out the parsing code into a common "Murata FRU ID" thingy. But, well...I don't think that actually gains us all that much, I just have some aesthetic desire to not duplicate things.
There was a problem hiding this comment.
I pulled it out into a mwocp6x module along with the shared types. What do you think of the new module structure? It might need to change later if the firmware update process ends up being significantly different, but hopefully not.
There was a problem hiding this comment.
It's unfortunate that we can't share more of the implementations - they look similar but use different types from the pmbus crate.
| // ACAN-157 doesn't specify what page this is on. | ||
| // Assume it's on page 0, as it is on the better-documented mwocp68. |
There was a problem hiding this comment.
Out of curiosity, have we tested what happens when we read this (and the other status registers) on page 1? Does it also work, or does the MCU get mad at us?
There was a problem hiding this comment.
I tried reading a bunch of these registers on both pages and always got the same value on both. I don't know if that's because the page is ignored or they happened to be the same on both pages.
| Slot::Psu5 => '5', | ||
| }; | ||
| FixedString::try_from_utf8(&v54_psu[..]).unwrap_lite() | ||
| Mwocp6x::rail_name(psu_label) |
There was a problem hiding this comment.
I found the decision to move this into the drivers a bit weird at first once I saw it, but now that I see how it's being used, it's not unreasonable. However, I don't love putting the rail name in the drv-i2c-devices crate, since we could imagine a universe where we have a MWOCP6x PSU on a board where we use different rail names (although admittedly, that's unlikely). Personally, I might have considered just leaving all the code here, and having one of two different "base" strings ("V54_PSUx" or "V50_PSUx") based on the board cfg. I'm not super bothered by the current approach, but I feel like it might be a bit simpler that way. Up to you!
There was a problem hiding this comment.
Yeah I don't love this approach either. On one hand it seems like the Mwocp67 class shouldn't need to know about rail naming conventions, and on the other hand scattering too many cfgs in the psc-seq-server could get confusing. So is this what you're suggesting?
let rail = {
#[cfg(any(target_board = "psc-b", target_board = "psc-c"))]
let mut rail_name = *b"V54_PSUx";
#[cfg(target_board = "observer-a")]
let mut rail_name = *b"V50_MAIN_PSUx";
rail_name[rail_name.len() - 1] = match self.slot {
Slot::Psu0 => b'0',
...
};
FixedString::try_from_utf8(&rail_name[..]).unwrap_lite()
};There was a problem hiding this comment.
Actually it's a problem that the rail names are different lengths now but EreportFields is still using FixedString<8>.
There was a problem hiding this comment.
Yeah, we will have to either just make it longer and null pad it on the MWOCP68 version, or make the const size depend on which board we're building. But I do think having all of this right there in that file feels better to me, rather than hiding it in the driver.
There was a problem hiding this comment.
FWIW I agree that the names should be here in psc-seq-server, rather than in the I2C driver (which should be rail-agnostic).
There was a problem hiding this comment.
Done. I should probably test this... any suggestions for how to generate an ereport and see if the rail name looks good?
There was a problem hiding this comment.
Tested! I generated a spurious ereport and read it with humility. The rail name looks good.
| Device::Tmp117 => { | ||
| for &s in self.temperature_sensors.iter() { | ||
| let m = Tmp117::new(&dev); | ||
| match m.read_temperature() { | ||
| Ok(v) => sensor_api.post_now(s, v.0), | ||
| Err(e) => { | ||
| let e = Error::Tmp117Error(e); | ||
| ringbuf_entry!(Trace::TemperatureReadFailed(s, e)); | ||
| sensor_api.nodata_now(s, e.into()) | ||
| } | ||
| } | ||
| } | ||
| // The tmp117 is just a temperature sensor, not a speed sensor. | ||
| assert_eq!(self.speed_sensors.len(), 0); | ||
| } |
There was a problem hiding this comment.
Am I correct that, after this PR merges, if we were to add a temperature sensor to the TMP117 in the PSC config, we would also be able to fix #2547?
There was a problem hiding this comment.
Yeah you'd need to update the PSC's base.toml, add an entry to the SENSORS array in sensor-polling, and test that the ComponentDetails part works (not just humility sensors).
This PR implements oxidecomputer/pmbus#28. It:
psc-seq-serverandobserver-seq-serverback into one task, since the differences can be managed by a fewcfgs. (I considered using anMwocp6Xtrait instead to make it generic over PSU models, but that's hard because of the methods that return device-specificCommandDatatypes).framulatortask to dev builds, to test that the FRAM works.Known issues / remaining todos:
Testing: