Add proc IO Ring and core/IO ring DVFS sensors

Add support for the processor IO ring temperature sensor which the OCC
uses fru type 9 for.  There is one per OCC.

Add support for reading the processor core and IO ring DVFS (Dynamic
Voltage and Frequency Slewing) temperature values out of the tempN_max
files and putting them on D-Bus.  These values are the temperatures at
which the OCC starts using DVFS to cool the chip and are being put on
D-Bus so that fan control can read them and increase fan speeds before
then so the OCC doesn't have to do the DVFS.

Even though there is a value in the tempN_max file for every core, there
only needs to be one value on D-Bus per OCC.  These are put in the
sensors namespace using the xyz.openbmc_project.Sensor.Value interface.
They do not need associations to the chassis, and they don't need to be
set to NaN when the OCCs aren't active, since the values can never
change.

There are values in the tempN_max files for most, if not all, other
temperature types as well, but the IO ring and core DVFS temps are the
only ones that aren't known ahead of time because they come from the
processor EEPROM.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I3e3f7a2a5db4053ea055e7baa16f0b0bf6eddbca
diff --git a/occ_dbus.cpp b/occ_dbus.cpp
index 0b44181..aaaf1c6 100644
--- a/occ_dbus.cpp
+++ b/occ_dbus.cpp
@@ -235,6 +235,18 @@
     return defaultChassisPath;
 }
 
+bool OccDBusSensors::hasDvfsTemp(const std::string& path) const
+{
+    return dvfsTemps.find(path) != dvfsTemps.end();
+}
+
+void OccDBusSensors::setDvfsTemp(const std::string& path, double value)
+{
+    dvfsTemps[path] =
+        std::make_unique<SensorIntf>(utils::getBus(), path.c_str());
+    dvfsTemps[path]->value(value);
+}
+
 } // namespace dbus
 } // namespace occ
 } // namespace open_power
diff --git a/occ_dbus.hpp b/occ_dbus.hpp
index 3938464..8d88b80 100644
--- a/occ_dbus.hpp
+++ b/occ_dbus.hpp
@@ -144,6 +144,20 @@
      */
     void setChassisAssociation(const std::string& path);
 
+    /** @brief Set the value of the DVFS temp sensor
+     *
+     *  @param[in] path  - The object path
+     *  @param[in] value - The value of the Value property
+     */
+    void setDvfsTemp(const std::string& path, double value);
+
+    /** @brief Says if the DVFS temp sensor is already present
+     *
+     *  @param[in] value - The value of the Value property
+     *  @return bool - If the sensor is already present
+     */
+    bool hasDvfsTemp(const std::string& path) const;
+
   private:
     std::map<ObjectPath, std::unique_ptr<SensorIntf>> sensors;
 
@@ -153,6 +167,13 @@
     std::map<ObjectPath, std::unique_ptr<AssociationIntf>> chassisAssociations;
 
     std::string chassisPath;
+
+    /** @brief Map of DVFS (Dynamic Voltage and Frequency Slewing) temps
+     *
+     * These do not have associations and do not get set to NaN when the OCC
+     * isn't active.
+     */
+    std::map<std::string, std::unique_ptr<SensorIntf>> dvfsTemps;
 };
 
 } // namespace dbus
diff --git a/occ_manager.cpp b/occ_manager.cpp
index ed69274..eadbca1 100644
--- a/occ_manager.cpp
+++ b/occ_manager.cpp
@@ -24,6 +24,7 @@
 constexpr auto fruTypeSuffix = "fru_type";
 constexpr auto faultSuffix = "fault";
 constexpr auto inputSuffix = "input";
+constexpr auto maxSuffix = "max";
 
 using namespace phosphor::logging;
 
@@ -492,10 +493,18 @@
         std::string sensorPath =
             OCC_SENSORS_ROOT + std::string("/temperature/");
 
+        std::string dvfsTempPath;
+
         if (fruTypeValue == VRMVdd)
         {
             sensorPath.append("vrm_vdd" + std::to_string(id) + "_temp");
         }
+        else if (fruTypeValue == processorIoRing)
+        {
+            sensorPath.append("proc" + std::to_string(id) + "_ioring_temp");
+            dvfsTempPath = std::string{OCC_SENSORS_ROOT} + "/temperature/proc" +
+                           std::to_string(id) + "_ioring_dvfs_temp";
+        }
         else
         {
             uint16_t type = (labelValue & 0xFF000000) >> 24;
@@ -525,20 +534,25 @@
             }
             else if (type == OCC_CPU_TEMP_SENSOR_TYPE)
             {
-                if (fruTypeValue != processorCore)
+                if (fruTypeValue == processorCore)
                 {
-                    // TODO: support IO ring temp
+                    // The OCC reports small core temps, of which there are
+                    // two per big core.  All current P10 systems are in big
+                    // core mode, so use a big core name.
+                    uint16_t coreNum = instanceID / 2;
+                    uint16_t tempNum = instanceID % 2;
+                    sensorPath.append("proc" + std::to_string(id) + "_core" +
+                                      std::to_string(coreNum) + "_" +
+                                      std::to_string(tempNum) + "_temp");
+
+                    dvfsTempPath = std::string{OCC_SENSORS_ROOT} +
+                                   "/temperature/proc" + std::to_string(id) +
+                                   "_core_dvfs_temp";
+                }
+                else
+                {
                     continue;
                 }
-
-                // The OCC reports small core temps, of which there are
-                // two per big core.  All current P10 systems are in big
-                // core mode, so use a big core name.
-                uint16_t coreNum = instanceID / 2;
-                uint16_t tempNum = instanceID % 2;
-                sensorPath.append("proc" + std::to_string(id) + "_core" +
-                                  std::to_string(coreNum) + "_" +
-                                  std::to_string(tempNum) + "_temp");
             }
             else
             {
@@ -546,6 +560,27 @@
             }
         }
 
+        // The dvfs temp file only needs to be read once per chip per type.
+        if (!dvfsTempPath.empty() &&
+            !dbus::OccDBusSensors::getOccDBus().hasDvfsTemp(dvfsTempPath))
+        {
+            try
+            {
+                auto dvfsValue = readFile<double>(filePathString + maxSuffix);
+
+                dbus::OccDBusSensors::getOccDBus().setDvfsTemp(
+                    dvfsTempPath, dvfsValue * std::pow(10, -3));
+            }
+            catch (const std::system_error& e)
+            {
+                log<level::DEBUG>(
+                    fmt::format(
+                        "readTempSensors: Failed reading {}, errno = {}",
+                        filePathString + maxSuffix, e.code().value())
+                        .c_str());
+            }
+        }
+
         uint32_t faultValue{0};
         try
         {
diff --git a/occ_manager.hpp b/occ_manager.hpp
index e6e7a18..742c222 100644
--- a/occ_manager.hpp
+++ b/occ_manager.hpp
@@ -36,7 +36,8 @@
     memCtrlAndDimm = 3,
     VRMVdd = 6,
     PMIC = 7,
-    memCtlrExSensor = 8
+    memCtlrExSensor = 8,
+    processorIoRing = 9
 };
 #endif