Add in support for power supply fan fault

If the FAN FAULT OR WARN bit in the STATUS_WORD turns on, log an error
calling out the power supply, include the contents from STATUS_WORD,
MFR_SPECIFIC, STATUS_TEMPERATURE, and STATUS_FANS_1_2 in the metadata.

Change-Id: Ic9261cf08517344f594a4616a91dbec47bb07d7e
Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
diff --git a/elog-errors.hpp b/elog-errors.hpp
index 892ce20..3f3e073 100644
--- a/elog-errors.hpp
+++ b/elog-errors.hpp
@@ -201,6 +201,26 @@
 {
 namespace Error
 {
+    struct PowerSupplyFanFault;
+} // namespace Error
+} // namespace Fault
+} // namespace Power
+} // namespace openbmc_project
+} // namespace xyz
+} // namespace sdbusplus
+
+namespace sdbusplus
+{
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Power
+{
+namespace Fault
+{
+namespace Error
+{
     struct PowerSupplyInputFault;
 } // namespace Error
 } // namespace Fault
@@ -859,6 +879,54 @@
 {
 namespace Fault
 {
+namespace _PowerSupplyFanFault
+{
+
+struct RAW_STATUS
+{
+    static constexpr auto str = "RAW_STATUS=%s";
+    static constexpr auto str_short = "RAW_STATUS";
+    using type = std::tuple<std::decay_t<decltype(str)>,const char*>;
+    explicit constexpr RAW_STATUS(const char* a) : _entry(entry(str, a)) {};
+    type _entry;
+};
+
+}  // namespace _PowerSupplyFanFault
+
+struct PowerSupplyFanFault
+{
+    static constexpr auto L = level::ERR;
+    using RAW_STATUS = _PowerSupplyFanFault::RAW_STATUS;
+    using CALLOUT_INVENTORY_PATH = xyz::openbmc_project::Common::Callout::Inventory::CALLOUT_INVENTORY_PATH;
+    using metadata_types = std::tuple<RAW_STATUS, CALLOUT_INVENTORY_PATH>;
+
+};
+
+} // namespace Fault
+} // namespace Power
+} // namespace openbmc_project
+} // namespace xyz
+
+
+namespace details
+{
+
+template <>
+struct map_exception_type<sdbusplus::xyz::openbmc_project::Power::Fault::Error::PowerSupplyFanFault>
+{
+    using type = xyz::openbmc_project::Power::Fault::PowerSupplyFanFault;
+};
+
+}
+
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Power
+{
+namespace Fault
+{
 namespace _Shutdown
 {
 
diff --git a/pmbus.hpp b/pmbus.hpp
index 23121b9..a2572f7 100644
--- a/pmbus.hpp
+++ b/pmbus.hpp
@@ -29,6 +29,13 @@
 // Manufacturing specific status bits
 constexpr auto STATUS_MFR = "status0_mfr";
 
+// Reports on the status of any fans installed in position 1 and 2.
+constexpr auto STATUS_FANS_1_2 = "status0_fans12";
+
+// Reports on temperature faults or warnings. Overtemperature fault,
+// overtemperature warning, undertemperature warning, undertemperature fault.
+constexpr auto STATUS_TEMPERATURE = "status0_temp";
+
 namespace status_word
 {
 constexpr auto VOUT_FAULT = 0x8000;
@@ -42,6 +49,10 @@
 // The bit mask representing the POWER_GOOD Negated bit of the STATUS_WORD.
 constexpr auto POWER_GOOD_NEGATED = 0x0800;
 
+// The bit mask representing the FAN FAULT or WARNING bit of the STATUS_WORD.
+// Bit 2 of the high byte of STATUS_WORD.
+constexpr auto FAN_FAULT = 0x0400;
+
 // The bit mask representing the UNITI_IS_OFF bit of the STATUS_WORD.
 constexpr auto UNIT_IS_OFF = 0x0040;
 
diff --git a/power-supply/power_supply.cpp b/power-supply/power_supply.cpp
index b3d556c..d9e870c 100644
--- a/power-supply/power_supply.cpp
+++ b/power-supply/power_supply.cpp
@@ -109,6 +109,7 @@
                 checkPGOrUnitOffFault(statusWord);
                 checkCurrentOutOverCurrentFault(statusWord);
                 checkOutputOvervoltageFault(statusWord);
+                checkFanFault(statusWord);
             }
         }
     }
@@ -143,6 +144,7 @@
             inputFault = false;
             outputOCFault = false;
             outputOVFault = false;
+            fanFault = false;
         }
     }
 
@@ -194,6 +196,7 @@
             powerOnFault = false;
             outputOCFault = false;
             outputOVFault = false;
+            fanFault = false;
             powerOnTimer.start(powerOnInterval, Timer::TimerType::oneshot);
         }
         else
@@ -418,6 +421,39 @@
     }
 }
 
+void PowerSupply::checkFanFault(const uint16_t statusWord)
+{
+    using namespace witherspoon::pmbus;
+
+    std::uint8_t statusMFR  = 0;
+    std::uint8_t statusTemperature = 0;
+    std::uint8_t statusFans12 = 0;
+
+    // Check for an output overcurrent fault.
+    if ((statusWord & status_word::FAN_FAULT) &&
+        !fanFault)
+    {
+        statusMFR = pmbusIntf.read(STATUS_MFR, Type::Debug);
+        statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
+        statusFans12 = pmbusIntf.read(STATUS_FANS_1_2, Type::Debug);
+
+        util::NamesValues nv;
+        nv.add("STATUS_WORD", statusWord);
+        nv.add("MFR_SPECIFIC", statusMFR);
+        nv.add("STATUS_TEMPERATURE", statusTemperature);
+        nv.add("STATUS_FANS_1_2", statusFans12);
+
+        using metadata = xyz::openbmc_project::Power::Fault::
+                PowerSupplyFanFault;
+
+        report<PowerSupplyFanFault>(
+                metadata::RAW_STATUS(nv.get().c_str()),
+                metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
+
+        fanFault = true;
+    }
+}
+
 void PowerSupply::clearFaults()
 {
     //TODO - Clear faults at pre-poweron. openbmc/openbmc#1736
diff --git a/power-supply/power_supply.hpp b/power-supply/power_supply.hpp
index 98c4c0b..a3cea2d 100644
--- a/power-supply/power_supply.hpp
+++ b/power-supply/power_supply.hpp
@@ -155,6 +155,11 @@
         bool outputOVFault = false;
 
         /**
+         * @brief Set to true when a fan fault or warning condition is detected
+         */
+        bool fanFault = false;
+
+        /**
          * @brief Callback for inventory property changes
          *
          * Process change of Present property for power supply.
@@ -225,6 +230,16 @@
          */
         void checkOutputOvervoltageFault(const uint16_t statusWord);
 
+        /**
+         * @brief Checks for a fan fault or warning condition.
+         *
+         * The high byte of STATUS_WORD is checked to see if the "FAN FAULT OR
+         * WARNING" bit is turned on. If it is on, log an error.
+         *
+         * @param[in] statusWord - 2 byte STATUS_WORD value read from sysfs
+         */
+        void checkFanFault(const uint16_t statusWord);
+
 };
 
 }
diff --git a/xyz/openbmc_project/Power/Fault.errors.yaml b/xyz/openbmc_project/Power/Fault.errors.yaml
index b08bfa1..9f2e770 100644
--- a/xyz/openbmc_project/Power/Fault.errors.yaml
+++ b/xyz/openbmc_project/Power/Fault.errors.yaml
@@ -8,6 +8,8 @@
   description: The power supply detected an output overcurrent fault condition.
 - name: PowerSupplyOutputOvervoltage
   description: The power supply detected an output overvoltage fault condition.
+- name: PowerSupplyFanFault
+  description: The power supply detected bad fan operation.
 - name: Shutdown
   description: A power off was issued because a power fault was detected
 
diff --git a/xyz/openbmc_project/Power/Fault.metadata.yaml b/xyz/openbmc_project/Power/Fault.metadata.yaml
index ab7eccb..b92da35 100644
--- a/xyz/openbmc_project/Power/Fault.metadata.yaml
+++ b/xyz/openbmc_project/Power/Fault.metadata.yaml
@@ -29,6 +29,13 @@
       type: string
   inherits:
     - xyz.openbmc_project.Common.Callout.Inventory
+- name: PowerSupplyFanFault
+  level: ERR
+  meta:
+    - str: "RAW_STATUS=%s"
+      type: string
+  inherits:
+    - xyz.openbmc_project.Common.Callout.Inventory
 - name: Shutdown
   level: ERR