Add PSU PWM sensor to support FSC

Add PSU PWM sensor to allow FSC control PSU fan pwm.

Tested:
Change property Target to 100 in dbus interface xyz.openbmc_project.Control.FanPwm
path /xyz/openbmc_project/control/fanpwm/Pwm_PSU1_Fan_1, the value in
/sys/class/hwmon/hwmonx/fan1_target also become 100, and value property in interface
/xyz/openbmc_project/sensors/fan_pwm/Pwm_PSU1_Fan_1 become 39.2157

Run ipmitool sensor list, bellow sensors will show like:
Pwm PSU1 Fan 1   | 39.200     | unspecified | ok    | na        | na        | na        | na        | na        | na
Pwm PSU2 Fan 1   | 0.000      | unspecified | ok    | na        | na        | na        | na        | na        | na

Signed-off-by: Cheng C Yang <cheng.c.yang@linux.intel.com>
Change-Id: I6a4d50912a190297dbcf4a4e5c511607f71cb0c0
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4276cf6..d0c6ce5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,7 +42,8 @@
 
 set (IPMB_SRC_FILES src/Utils.cpp src/Thresholds.cpp)
 
-set (PSU_SRC_FILES src/Utils.cpp src/PSUSensor.cpp src/Thresholds.cpp)
+set (PSU_SRC_FILES src/Utils.cpp src/PSUSensor.cpp src/Thresholds.cpp
+     src/PwmSensor.cpp)
 
 set (EXTERNAL_PACKAGES Boost sdbusplus-project nlohmann-json)
 set (SENSOR_LINK_LIBS -lsystemd stdc++fs sdbusplus)
diff --git a/include/PSUSensor.hpp b/include/PSUSensor.hpp
index 0c4edb1..afc748b 100644
--- a/include/PSUSensor.hpp
+++ b/include/PSUSensor.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <PwmSensor.hpp>
 #include <Thresholds.hpp>
 #include <sdbusplus/asio/object_server.hpp>
 #include <sensor.hpp>
diff --git a/include/PwmSensor.hpp b/include/PwmSensor.hpp
index 67aff56..973d695 100644
--- a/include/PwmSensor.hpp
+++ b/include/PwmSensor.hpp
@@ -5,7 +5,7 @@
 class PwmSensor
 {
   public:
-    PwmSensor(const std::string& sysPath,
+    PwmSensor(const std::string& name, const std::string& sysPath,
               sdbusplus::asio::object_server& objectServer,
               const std::string& sensorConfiguration);
     ~PwmSensor();
diff --git a/src/FanMain.cpp b/src/FanMain.cpp
index 866fe4f..962a115 100644
--- a/src/FanMain.cpp
+++ b/src/FanMain.cpp
@@ -314,9 +314,12 @@
         }
 
         // only add new elements
+        const std::string& sysPath = pwm.string();
+        const std::string& pwmName =
+            "Pwm_" + sysPath.substr(sysPath.find_last_of("pwm") + 1);
         pwmSensors.insert(std::pair<std::string, std::unique_ptr<PwmSensor>>(
-            pwm.string(),
-            std::make_unique<PwmSensor>(pwm.string(), objectServer, *path)));
+            sysPath, std::make_unique<PwmSensor>(pwmName, sysPath, objectServer,
+                                                 *path)));
     }
 }
 
diff --git a/src/PSUSensorMain.cpp b/src/PSUSensorMain.cpp
index 8f36b86..667dcca 100644
--- a/src/PSUSensorMain.cpp
+++ b/src/PSUSensorMain.cpp
@@ -30,13 +30,50 @@
 
 namespace fs = std::filesystem;
 
-void createSensors(
-    boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
-    std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
-    boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>>&
-        sensors,
-    boost::container::flat_map<std::string, std::string>& sensorTable,
-    boost::container::flat_map<std::string, PSUProperty>& labelMatch)
+static boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>>
+    sensors;
+static boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
+    pwmSensors;
+static boost::container::flat_map<std::string, std::string> sensorTable;
+static boost::container::flat_map<std::string, PSUProperty> labelMatch;
+static boost::container::flat_map<std::string, std::string> pwmTable;
+
+static void checkPWMSensor(const fs::path& sensorPath, std::string& labelHead,
+                           const std::string& interfacePath,
+                           sdbusplus::asio::object_server& objectServer,
+                           std::string psuName)
+{
+    for (const auto& pwmName : pwmTable)
+    {
+        if (pwmName.first != labelHead)
+        {
+            continue;
+        }
+
+        const std::string& sensorPathStr = sensorPath.string();
+        const std::string& pwmPathStr =
+            boost::replace_all_copy(sensorPathStr, "input", "target");
+        std::ifstream pwmFile(pwmPathStr);
+        if (!pwmFile.good())
+        {
+            continue;
+        }
+
+        auto findPWMSensor = pwmSensors.find(psuName + labelHead);
+        if (findPWMSensor != pwmSensors.end())
+        {
+            continue;
+        }
+
+        pwmSensors[psuName + labelHead] = std::make_unique<PwmSensor>(
+            "Pwm_" + psuName + "_" + pwmName.second, pwmPathStr, objectServer,
+            interfacePath + "/" + psuName + " " + pwmName.second);
+    }
+}
+
+void createSensors(boost::asio::io_service& io,
+                   sdbusplus::asio::object_server& objectServer,
+                   std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
 {
 
     ManagedObjectType sensorConfigs;
@@ -224,6 +261,9 @@
                 labelHead = label.substr(0, label.find(" "));
             }
 
+            checkPWMSensor(sensorPath, labelHead, *interfacePath, objectServer,
+                           std::get<std::string>(findPSUName->second));
+
             std::vector<thresholds::Threshold> sensorThresholds;
 
             parseThresholdsFromConfig(*sensorData, sensorThresholds,
@@ -269,9 +309,7 @@
     return;
 }
 
-void propertyInitialize(
-    boost::container::flat_map<std::string, std::string>& sensorTable,
-    boost::container::flat_map<std::string, PSUProperty>& labelMatch)
+void propertyInitialize(void)
 {
     sensorTable = {{"power", "power/"},
                    {"curr", "current/"},
@@ -287,6 +325,8 @@
                   {"temp1", PSUProperty("Temperature", 127, -128, 3)},
                   {"fan1", PSUProperty("Fan Speed 1", 10000, 0, 0)},
                   {"fan2", PSUProperty("Fan Speed 2", 10000, 0, 0)}};
+
+    pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
 }
 
 int main(int argc, char** argv)
@@ -296,17 +336,11 @@
 
     systemBus->request_name("xyz.openbmc_project.PSUSensor");
     sdbusplus::asio::object_server objectServer(systemBus);
-    boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>> sensors;
-    boost::container::flat_map<std::string, std::string> sensorTable;
     std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
-    boost::container::flat_map<std::string, PSUProperty> labelMatch;
 
-    propertyInitialize(sensorTable, labelMatch);
+    propertyInitialize();
 
-    io.post([&]() {
-        createSensors(io, objectServer, systemBus, sensors, sensorTable,
-                      labelMatch);
-    });
+    io.post([&]() { createSensors(io, objectServer, systemBus); });
     boost::asio::deadline_timer filterTimer(io);
     std::function<void(sdbusplus::message::message&)> eventHandler =
         [&](sdbusplus::message::message& message) {
@@ -325,8 +359,7 @@
                 {
                     std::cerr << "timer error\n";
                 }
-                createSensors(io, objectServer, systemBus, sensors, sensorTable,
-                              labelMatch);
+                createSensors(io, objectServer, systemBus);
             });
         };
 
diff --git a/src/PwmSensor.cpp b/src/PwmSensor.cpp
index b157d52..6402ad0 100644
--- a/src/PwmSensor.cpp
+++ b/src/PwmSensor.cpp
@@ -24,15 +24,12 @@
 static constexpr size_t pwmMax = 255;
 static constexpr size_t pwmMin = 0;
 
-PwmSensor::PwmSensor(const std::string& sysPath,
+PwmSensor::PwmSensor(const std::string& name, const std::string& sysPath,
                      sdbusplus::asio::object_server& objectServer,
                      const std::string& sensorConfiguration) :
-    sysPath(sysPath),
-    objectServer(objectServer)
+    name(name),
+    sysPath(sysPath), objectServer(objectServer)
 {
-    // strip off index from path
-    name = "Pwm_" + sysPath.substr(sysPath.find_last_of("pwm") + 1);
-
     // add interface under sensor and Control.FanPwm as Control is used
     // in obmc project, also add sensor so it can be viewed as a sensor
     sensorInterface = objectServer.add_interface(