psu-ng: Put input voltage rating on D-Bus

IBM systems must tell the BIOS (called hostboot) the power supply input
voltage rating during a boot, where the rating is 110 or 220.  This is
done via PLDM BIOS attributes where the attribute is backed by a D-Bus
property.

This commit adds support for that by putting the input voltage rating
of each PS on D-Bus using the Value property of the
xyz.openbmc_project.Sensor.Value interface. The path is like
/xyz/openbmc_project/sensors/voltage/ps0_input_voltage_rating.

$ busctl get-property xyz.openbmc_project.Power.PSUMonitor \
  /xyz/openbmc_project/sensors/voltage/ps0_input_voltage_rating \
  xyz.openbmc_project.Sensor.Value Value
d 220

$ busctl get-property xyz.openbmc_project.Power.PSUMonitor \
  /xyz/openbmc_project/sensors/voltage/ps0_input_voltage_rating \
  xyz.openbmc_project.Sensor.Value Unit
s "xyz.openbmc_project.Sensor.Value.Unit.Volts"

These D-Bus objects won't have any associations, unlike normal sensors,
so they will not show up in any Redfish output as a sensor.

The interface is only created for present power supplies, and is only
updated when the application starts up and when the boot starts.

If a power supply is hot added or removed after that, the D-Bus property
doesn't need to be updated as hostboot has already consumed it.

Change-Id: I277516e8d86f1ba1a75b75fe76c67f192d911f88
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/phosphor-power-supply/power_supply.cpp b/phosphor-power-supply/power_supply.cpp
index 9d884f3..cea2ba2 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -96,6 +96,8 @@
         updateInventory();
         setupInputHistory();
     }
+
+    setInputVoltageRating();
 }
 
 void PowerSupply::bindOrUnbindDriver(bool present)
@@ -1181,4 +1183,37 @@
     }
 }
 
+void PowerSupply::setInputVoltageRating()
+{
+    if (!present)
+    {
+        return;
+    }
+
+    double inputVoltageValue{};
+    int inputVoltageRating{};
+    getInputVoltage(inputVoltageValue, inputVoltageRating);
+
+    if (!inputVoltageRatingIface)
+    {
+        auto path = fmt::format(
+            "/xyz/openbmc_project/sensors/voltage/ps{}_input_voltage_rating",
+            shortName.back());
+
+        inputVoltageRatingIface = std::make_unique<SensorObject>(
+            bus, path.c_str(), SensorObject::action::defer_emit);
+
+        // Leave other properties at their defaults
+        inputVoltageRatingIface->unit(SensorInterface::Unit::Volts, true);
+        inputVoltageRatingIface->value(static_cast<double>(inputVoltageRating),
+                                       true);
+
+        inputVoltageRatingIface->emit_object_added();
+    }
+    else
+    {
+        inputVoltageRatingIface->value(static_cast<double>(inputVoltageRating));
+    }
+}
+
 } // namespace phosphor::power::psu
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
index d823e68..da6adad 100644
--- a/phosphor-power-supply/power_supply.hpp
+++ b/phosphor-power-supply/power_supply.hpp
@@ -10,6 +10,7 @@
 
 #include <gpiod.hpp>
 #include <sdbusplus/bus/match.hpp>
+#include <xyz/openbmc_project/Sensor/Value/server.hpp>
 
 #include <filesystem>
 #include <stdexcept>
@@ -49,6 +50,9 @@
 // than PGOOD_DEGLITCH_LIMIT.
 constexpr auto AC_FAULT_LIMIT = 6;
 
+using SensorInterface = sdbusplus::xyz::openbmc_project::Sensor::server::Value;
+using SensorObject = sdbusplus::server::object_t<SensorInterface>;
+
 /**
  * @class PowerSupply
  * Represents a PMBus power supply device.
@@ -531,6 +535,13 @@
         syncHistoryRequired = false;
     }
 
+    /**
+     * @brief Puts the input voltage rating on D-Bus.
+     *
+     * The rating is like 0, 110, 220.
+     */
+    void setInputVoltageRating();
+
   private:
     /**
      * @brief systemd bus member
@@ -1018,6 +1029,15 @@
      * objects.
      **/
     std::string historyObjectPath;
+
+    /**
+     * @brief The D-Bus object for the input voltage rating
+     *
+     * It is updated at startup and power on.  If a power supply is
+     * added or removed after that, it does not need to be updated
+     * again (though that could be done as a future improvement).
+     */
+    std::unique_ptr<SensorObject> inputVoltageRatingIface;
 };
 
 } // namespace phosphor::power::psu
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index 68ff7fb..62501e9 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -39,7 +39,8 @@
 PSUManager::PSUManager(sdbusplus::bus_t& bus, const sdeventplus::Event& e) :
     bus(bus), powerSystemInputs(bus, powerSystemsInputsObjPath),
     objectManager(bus, objectManagerObjPath),
-    historyManager(bus, "/org/open_power/sensors")
+    historyManager(bus, "/org/open_power/sensors"),
+    sensorsObjManager(bus, "/xyz/openbmc_project/sensors")
 {
     // Subscribe to InterfacesAdded before doing a property read, otherwise
     // the interface could be created after the read attempt but before the
@@ -429,6 +430,7 @@
             clearFaults();
             syncHistory();
             setPowerConfigGPIO();
+            setInputVoltageRating();
         }
         else
         {
diff --git a/phosphor-power-supply/psu_manager.hpp b/phosphor-power-supply/psu_manager.hpp
index 0749185..8acb416 100644
--- a/phosphor-power-supply/psu_manager.hpp
+++ b/phosphor-power-supply/psu_manager.hpp
@@ -370,6 +370,15 @@
     sdbusplus::server::manager_t historyManager;
 
     /**
+     * @brief Implement the ObjectManager for the input voltage rating.
+     *
+     * Implements the org.freedesktop.DBus.ObjectManager interface used to
+     * communicate updates to the input voltage ratings on the
+     * /xyz/openbmc_project/sensors root D-Bus path.
+     */
+    sdbusplus::server::manager_t sensorsObjManager;
+
+    /**
      * @brief GPIO to toggle to 'sync' power supply input history.
      */
     std::unique_ptr<GPIOInterfaceBase> syncHistoryGPIO = nullptr;
@@ -387,6 +396,18 @@
      * start fresh.
      */
     void syncHistory();
+
+    /**
+     * @brief Tells each PSU to set its power supply input
+     *        voltage rating D-Bus property.
+     */
+    inline void setInputVoltageRating()
+    {
+        for (auto& psu : psus)
+        {
+            psu->setInputVoltageRating();
+        }
+    }
 };
 
 } // namespace phosphor::power::manager