nvidia-gpu: add Power Sensor PeakReading Property
Add support for Sensor Properties PeakReading and PeakRedingTime.
Current Limitation -
The ResetMetrics action is currently not supported for Redfish URIs in
bmcweb. As a result, the ability to clear PeakReading values for GPU
Power Sensors has not been implemented.
Future Consideration -
If ResetMetrics action support is added to bmcweb in the future, the
corresponding functionality will also need to be implemented in the
dbus-sensor application to ensure full compatibility.
Tested: Build an image for gb200nvl-obmc machine with the following
patches cherry picked. This patches are needed to enable the mctp stack.
https://gerrit.openbmc.org/c/openbmc/openbmc/+/79422
```
root@gb200nvl-obmc:~# busctl introspect xyz.openbmc_project.GpuSensor /xyz/openbmc_project/sensors/power/NVIDIA_GB200_GPU_0_Power_0
NAME                                                  TYPE      SIGNATURE  RESULT/VALUE                             FLAGS
org.freedesktop.DBus.Introspectable                   interface -          -                                        -
.Introspect                                           method    -          s                                        -
org.freedesktop.DBus.Peer                             interface -          -                                        -
.GetMachineId                                         method    -          s                                        -
.Ping                                                 method    -          -                                        -
org.freedesktop.DBus.Properties                       interface -          -                                        -
.Get                                                  method    ss         v                                        -
.GetAll                                               method    s          a{sv}                                    -
.Set                                                  method    ssv        -                                        -
.PropertiesChanged                                    signal    sa{sv}as   -                                        -
xyz.openbmc_project.Association.Definitions           interface -          -                                        -
.Associations                                         property  a(sss)     1 "chassis" "all_sensors" "/xyz/openb... emits-change
xyz.openbmc_project.Sensor.Value                      interface -          -                                        -
.MaxValue                                             property  d          5000                                     emits-change
.MinValue                                             property  d          0                                        emits-change
.Unit                                                 property  s          "xyz.openbmc_project.Sensor.Value.Uni... emits-change
.Value                                                property  d          29.194                                   emits-change writable
xyz.openbmc_project.Sensor.ValueMutability            interface -          -                                        -
.Mutable                                              property  b          true                                     emits-change
xyz.openbmc_project.State.Decorator.Availability      interface -          -                                        -
.Available                                            property  b          true                                     emits-change writable
xyz.openbmc_project.State.Decorator.OperationalStatus interface -          -                                        -
.Functional                                           property  b          true                                     emits-change
xyz.openbmc_project.Telemetry.Report                  interface -          -                                        -
.Readings                                             property  (ta(ssdt)) 0 1 "PeakReading" "" 80.933 0            emits-change
```
Change-Id: I0a4f7eb0a5db688f32bf80954839140da9bb7e2a
Signed-off-by: Harshit Aghera <haghera@nvidia.com>
diff --git a/src/nvidia-gpu/NvidiaGpuPowerPeakReading.cpp b/src/nvidia-gpu/NvidiaGpuPowerPeakReading.cpp
new file mode 100644
index 0000000..06693e6
--- /dev/null
+++ b/src/nvidia-gpu/NvidiaGpuPowerPeakReading.cpp
@@ -0,0 +1,102 @@
+/*
+ * SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION &
+ * AFFILIATES. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "NvidiaGpuPowerPeakReading.hpp"
+
+#include "MctpRequester.hpp"
+#include "Utils.hpp"
+
+#include <bits/basic_string.h>
+
+#include <NvidiaDeviceDiscovery.hpp>
+#include <NvidiaGpuMctpVdm.hpp>
+#include <OcpMctpVdm.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <string>
+
+using namespace std::literals;
+
+NvidiaGpuPowerPeakReading::NvidiaGpuPowerPeakReading(
+    mctp::MctpRequester& mctpRequester, const std::string& name, uint8_t eid,
+    uint8_t sensorId, sdbusplus::asio::object_server& objectServer) :
+    eid(eid), sensorId{sensorId}, mctpRequester(mctpRequester),
+    objectServer(objectServer)
+{
+    std::string dbusPath = sensorPathPrefix + "power/"s + escapeName(name);
+
+    telemetryReportInterface = objectServer.add_interface(
+        dbusPath, "xyz.openbmc_project.Telemetry.Report");
+
+    std::get<0>(readings) = 0;
+    // Reading from the device is in milliwatts and unit set on the dbus
+    // is watts.
+    std::get<1>(readings).emplace_back("PeakReading", "", 0.0, 0);
+
+    telemetryReportInterface->register_property("Readings", readings);
+
+    telemetryReportInterface->initialize();
+}
+
+NvidiaGpuPowerPeakReading::~NvidiaGpuPowerPeakReading()
+{
+    objectServer.remove_interface(telemetryReportInterface);
+}
+
+void NvidiaGpuPowerPeakReading::processResponse(int sendRecvMsgResult)
+{
+    if (sendRecvMsgResult != 0)
+    {
+        lg2::error(
+            "Error updating Peak Power Sensor for eid {EID} and sensor id {SID} : sending message over MCTP failed, rc={RC}",
+            "EID", eid, "SID", sensorId, "RC", sendRecvMsgResult);
+        return;
+    }
+
+    ocp::accelerator_management::CompletionCode cc{};
+    uint16_t reasonCode = 0;
+    uint32_t peakPower = 0;
+
+    const int rc =
+        gpu::decodeGetPowerDrawResponse(response, cc, reasonCode, peakPower);
+
+    if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
+    {
+        lg2::error(
+            "Error updating Peak Power Sensor eid {EID} and sensor id {SID} : decode failed, rc={RC}, cc={CC}, reasonCode={RESC}",
+            "EID", eid, "SID", sensorId, "RC", rc, "CC", cc, "RESC",
+            reasonCode);
+        return;
+    }
+
+    // Reading from the device is in milliwatts and unit set on the dbus
+    // is watts.
+    std::get<2>(std::get<1>(readings)[0]) = peakPower / 1000.0;
+
+    telemetryReportInterface->set_property("Readings", readings);
+}
+
+void NvidiaGpuPowerPeakReading::update()
+{
+    const int rc = gpu::encodeGetPowerDrawRequest(
+        gpu::PlatformEnvironmentalCommands::GET_MAX_OBSERVED_POWER, 0, sensorId,
+        averagingInterval, request);
+
+    if (rc != 0)
+    {
+        lg2::error(
+            "Error updating Peak Power Sensor for eid {EID} and sensor id {SID} : encode failed, rc={RC}",
+            "EID", eid, "SID", sensorId, "RC", rc);
+    }
+
+    mctpRequester.sendRecvMsg(
+        eid, request, response,
+        [this](int sendRecvMsgResult) { processResponse(sendRecvMsgResult); });
+}