Update analyze to check VIN_UV_FAULT

The function is a pure virtual function in DeviceMonitor, add in the
implementation of that for PowerSupply. Read the file that represents
that bit from the STATUS_WORD. If fault is on, report a fault.

Change-Id: I05a4bff997bb0c8b8b71db444e9db0e506765689
Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
diff --git a/elog-errors.hpp b/elog-errors.hpp
index 3f7936e..7ea5cf3 100644
--- a/elog-errors.hpp
+++ b/elog-errors.hpp
@@ -55,6 +55,66 @@
 {
 namespace openbmc_project
 {
+namespace Common
+{
+namespace Callout
+{
+namespace Error
+{
+    struct GPIO;
+} // namespace Error
+} // namespace Callout
+} // namespace Common
+} // namespace openbmc_project
+} // namespace xyz
+} // namespace sdbusplus
+
+namespace sdbusplus
+{
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Common
+{
+namespace Callout
+{
+namespace Error
+{
+    struct Inventory;
+} // namespace Error
+} // namespace Callout
+} // namespace Common
+} // namespace openbmc_project
+} // namespace xyz
+} // namespace sdbusplus
+
+namespace sdbusplus
+{
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Common
+{
+namespace Callout
+{
+namespace Error
+{
+    struct IIC;
+} // namespace Error
+} // namespace Callout
+} // namespace Common
+} // namespace openbmc_project
+} // namespace xyz
+} // namespace sdbusplus
+
+namespace sdbusplus
+{
+namespace xyz
+{
+namespace openbmc_project
+{
 namespace Power
 {
 namespace Fault
@@ -75,6 +135,46 @@
 {
 namespace openbmc_project
 {
+namespace Common
+{
+namespace Callout
+{
+namespace Error
+{
+    struct Device;
+} // namespace Error
+} // namespace Callout
+} // namespace Common
+} // namespace openbmc_project
+} // namespace xyz
+} // namespace sdbusplus
+
+namespace sdbusplus
+{
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Power
+{
+namespace Fault
+{
+namespace Error
+{
+    struct PowerSupplyUnderVoltageFault;
+} // namespace Error
+} // namespace Fault
+} // namespace Power
+} // namespace openbmc_project
+} // namespace xyz
+} // namespace sdbusplus
+
+namespace sdbusplus
+{
+namespace xyz
+{
+namespace openbmc_project
+{
 namespace Power
 {
 namespace Fault
@@ -115,6 +215,26 @@
 {
 namespace openbmc_project
 {
+namespace Common
+{
+namespace Callout
+{
+namespace Error
+{
+    struct IPMISensor;
+} // namespace Error
+} // namespace Callout
+} // namespace Common
+} // namespace openbmc_project
+} // namespace xyz
+} // namespace sdbusplus
+
+namespace sdbusplus
+{
+namespace xyz
+{
+namespace openbmc_project
+{
 namespace Power
 {
 namespace Fault
@@ -160,6 +280,301 @@
 {
 namespace openbmc_project
 {
+namespace Common
+{
+namespace Callout
+{
+namespace _Device
+{
+
+struct CALLOUT_ERRNO
+{
+    static constexpr auto str = "CALLOUT_ERRNO=%d";
+    static constexpr auto str_short = "CALLOUT_ERRNO";
+    using type = std::tuple<std::decay_t<decltype(str)>,int32_t>;
+    explicit constexpr CALLOUT_ERRNO(int32_t a) : _entry(entry(str, a)) {};
+    type _entry;
+};
+struct CALLOUT_DEVICE_PATH
+{
+    static constexpr auto str = "CALLOUT_DEVICE_PATH=%s";
+    static constexpr auto str_short = "CALLOUT_DEVICE_PATH";
+    using type = std::tuple<std::decay_t<decltype(str)>,const char*>;
+    explicit constexpr CALLOUT_DEVICE_PATH(const char* a) : _entry(entry(str, a)) {};
+    type _entry;
+};
+
+}  // namespace _Device
+
+struct Device
+{
+    static constexpr auto L = level::ERR;
+    using CALLOUT_ERRNO = _Device::CALLOUT_ERRNO;
+    using CALLOUT_DEVICE_PATH = _Device::CALLOUT_DEVICE_PATH;
+    using metadata_types = std::tuple<CALLOUT_ERRNO, CALLOUT_DEVICE_PATH>;
+
+};
+
+} // namespace Callout
+} // namespace Common
+} // namespace openbmc_project
+} // namespace xyz
+
+
+namespace details
+{
+
+template <>
+struct map_exception_type<sdbusplus::xyz::openbmc_project::Common::Callout::Error::Device>
+{
+    using type = xyz::openbmc_project::Common::Callout::Device;
+};
+
+}
+
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Common
+{
+namespace Callout
+{
+namespace _GPIO
+{
+
+struct CALLOUT_GPIO_NUM
+{
+    static constexpr auto str = "CALLOUT_GPIO_NUM=%u";
+    static constexpr auto str_short = "CALLOUT_GPIO_NUM";
+    using type = std::tuple<std::decay_t<decltype(str)>,uint32_t>;
+    explicit constexpr CALLOUT_GPIO_NUM(uint32_t a) : _entry(entry(str, a)) {};
+    type _entry;
+};
+
+}  // namespace _GPIO
+
+struct GPIO
+{
+    static constexpr auto L = level::ERR;
+    using CALLOUT_GPIO_NUM = _GPIO::CALLOUT_GPIO_NUM;
+    using CALLOUT_ERRNO = xyz::openbmc_project::Common::Callout::Device::CALLOUT_ERRNO;
+    using CALLOUT_DEVICE_PATH = xyz::openbmc_project::Common::Callout::Device::CALLOUT_DEVICE_PATH;
+    using metadata_types = std::tuple<CALLOUT_GPIO_NUM, CALLOUT_ERRNO, CALLOUT_DEVICE_PATH>;
+
+};
+
+} // namespace Callout
+} // namespace Common
+} // namespace openbmc_project
+} // namespace xyz
+
+
+namespace details
+{
+
+template <>
+struct map_exception_type<sdbusplus::xyz::openbmc_project::Common::Callout::Error::GPIO>
+{
+    using type = xyz::openbmc_project::Common::Callout::GPIO;
+};
+
+}
+
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Common
+{
+namespace Callout
+{
+namespace _IIC
+{
+
+struct CALLOUT_IIC_BUS
+{
+    static constexpr auto str = "CALLOUT_IIC_BUS=%s";
+    static constexpr auto str_short = "CALLOUT_IIC_BUS";
+    using type = std::tuple<std::decay_t<decltype(str)>,const char*>;
+    explicit constexpr CALLOUT_IIC_BUS(const char* a) : _entry(entry(str, a)) {};
+    type _entry;
+};
+struct CALLOUT_IIC_ADDR
+{
+    static constexpr auto str = "CALLOUT_IIC_ADDR=0x%hx";
+    static constexpr auto str_short = "CALLOUT_IIC_ADDR";
+    using type = std::tuple<std::decay_t<decltype(str)>,uint16_t>;
+    explicit constexpr CALLOUT_IIC_ADDR(uint16_t a) : _entry(entry(str, a)) {};
+    type _entry;
+};
+
+}  // namespace _IIC
+
+struct IIC
+{
+    static constexpr auto L = level::ERR;
+    using CALLOUT_IIC_BUS = _IIC::CALLOUT_IIC_BUS;
+    using CALLOUT_IIC_ADDR = _IIC::CALLOUT_IIC_ADDR;
+    using CALLOUT_ERRNO = xyz::openbmc_project::Common::Callout::Device::CALLOUT_ERRNO;
+    using CALLOUT_DEVICE_PATH = xyz::openbmc_project::Common::Callout::Device::CALLOUT_DEVICE_PATH;
+    using metadata_types = std::tuple<CALLOUT_IIC_BUS, CALLOUT_IIC_ADDR, CALLOUT_ERRNO, CALLOUT_DEVICE_PATH>;
+
+};
+
+} // namespace Callout
+} // namespace Common
+} // namespace openbmc_project
+} // namespace xyz
+
+
+namespace details
+{
+
+template <>
+struct map_exception_type<sdbusplus::xyz::openbmc_project::Common::Callout::Error::IIC>
+{
+    using type = xyz::openbmc_project::Common::Callout::IIC;
+};
+
+}
+
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Common
+{
+namespace Callout
+{
+namespace _Inventory
+{
+
+struct CALLOUT_INVENTORY_PATH
+{
+    static constexpr auto str = "CALLOUT_INVENTORY_PATH=%s";
+    static constexpr auto str_short = "CALLOUT_INVENTORY_PATH";
+    using type = std::tuple<std::decay_t<decltype(str)>,const char*>;
+    explicit constexpr CALLOUT_INVENTORY_PATH(const char* a) : _entry(entry(str, a)) {};
+    type _entry;
+};
+
+}  // namespace _Inventory
+
+struct Inventory
+{
+    static constexpr auto L = level::ERR;
+    using CALLOUT_INVENTORY_PATH = _Inventory::CALLOUT_INVENTORY_PATH;
+    using metadata_types = std::tuple<CALLOUT_INVENTORY_PATH>;
+
+};
+
+} // namespace Callout
+} // namespace Common
+} // namespace openbmc_project
+} // namespace xyz
+
+
+namespace details
+{
+
+template <>
+struct map_exception_type<sdbusplus::xyz::openbmc_project::Common::Callout::Error::Inventory>
+{
+    using type = xyz::openbmc_project::Common::Callout::Inventory;
+};
+
+}
+
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Common
+{
+namespace Callout
+{
+namespace _IPMISensor
+{
+
+struct CALLOUT_IPMI_SENSOR_NUM
+{
+    static constexpr auto str = "CALLOUT_IPMI_SENSOR_NUM=%u";
+    static constexpr auto str_short = "CALLOUT_IPMI_SENSOR_NUM";
+    using type = std::tuple<std::decay_t<decltype(str)>,uint32_t>;
+    explicit constexpr CALLOUT_IPMI_SENSOR_NUM(uint32_t a) : _entry(entry(str, a)) {};
+    type _entry;
+};
+
+}  // namespace _IPMISensor
+
+struct IPMISensor
+{
+    static constexpr auto L = level::ERR;
+    using CALLOUT_IPMI_SENSOR_NUM = _IPMISensor::CALLOUT_IPMI_SENSOR_NUM;
+    using metadata_types = std::tuple<CALLOUT_IPMI_SENSOR_NUM>;
+
+};
+
+} // namespace Callout
+} // namespace Common
+} // namespace openbmc_project
+} // namespace xyz
+
+
+namespace details
+{
+
+template <>
+struct map_exception_type<sdbusplus::xyz::openbmc_project::Common::Callout::Error::IPMISensor>
+{
+    using type = xyz::openbmc_project::Common::Callout::IPMISensor;
+};
+
+}
+
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Power
+{
+namespace Fault
+{
+namespace _PowerSupplyUnderVoltageFault
+{
+
+
+}  // namespace _PowerSupplyUnderVoltageFault
+
+struct PowerSupplyUnderVoltageFault
+{
+    static constexpr auto L = level::ERR;
+    using metadata_types = std::tuple<>;
+
+};
+
+} // namespace Fault
+} // namespace Power
+} // namespace openbmc_project
+} // namespace xyz
+
+
+namespace details
+{
+
+template <>
+struct map_exception_type<sdbusplus::xyz::openbmc_project::Power::Fault::Error::PowerSupplyUnderVoltageFault>
+{
+    using type = xyz::openbmc_project::Power::Fault::PowerSupplyUnderVoltageFault;
+};
+
+}
+
+namespace xyz
+{
+namespace openbmc_project
+{
 namespace Power
 {
 namespace Fault
diff --git a/pmbus.hpp b/pmbus.hpp
index b23ab5e..bbe32e4 100644
--- a/pmbus.hpp
+++ b/pmbus.hpp
@@ -12,6 +12,8 @@
 namespace fs = std::experimental::filesystem;
 
 constexpr auto STATUS_WORD = "status0";
+// The file name Linux uses to capture the VIN_UV_FAULT bit from the STATUS_WORD
+constexpr auto VIN_UV_FAULT = "in1_alarm";
 
 // Uses Page substitution
 constexpr auto STATUS_VOUT = "statusP_vout";
diff --git a/power-supply/main.cpp b/power-supply/main.cpp
index eb3f9ed..e36bd24 100644
--- a/power-supply/main.cpp
+++ b/power-supply/main.cpp
@@ -56,13 +56,16 @@
         return -4;
     }
 
+    auto bus = sdbusplus::bus::new_default();
+
     auto objname = "power_supply" + instnum;
     auto instance = std::stoul(instnum);
     auto psuDevice = std::make_unique<psu::PowerSupply>(objname,
                                                         std::move(instance),
                                                         std::move(objpath),
-                                                        std::move(invpath));
-    auto bus = sdbusplus::bus::new_default();
+                                                        std::move(invpath),
+                                                        bus);
+
     sd_event* events = nullptr;
 
     auto r = sd_event_default(&events);
@@ -81,6 +84,14 @@
 
     // TODO: Use inventory path to subscribe to signal change for power supply presence.
 
+    //Attach the event object to the bus object so we can
+    //handle both sd_events (for the timers) and dbus signals.
+    bus.attach_event(eventPtr.get(), SD_EVENT_PRIORITY_NORMAL);
+
+    // TODO: Get power state on startup.
+    // TODO: Get presence state on startup and subscribe to presence changes.
+    // TODO - set to vinUVFault to false on presence change & start of poweron
+    // TODO - set readFailLogged to false on presence change and start of poweron
     auto pollInterval = std::chrono::milliseconds(1000);
     DeviceMonitor mainloop(std::move(psuDevice), eventPtr, pollInterval);
     mainloop.run();
diff --git a/power-supply/power_supply.cpp b/power-supply/power_supply.cpp
index e9f5d6b..8dd4c3e 100644
--- a/power-supply/power_supply.cpp
+++ b/power-supply/power_supply.cpp
@@ -13,7 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <xyz/openbmc_project/Sensor/Device/error.hpp>
+#include <xyz/openbmc_project/Control/Device/error.hpp>
+#include <xyz/openbmc_project/Power/Fault/error.hpp>
+#include "elog-errors.hpp"
 #include "power_supply.hpp"
+#include "pmbus.hpp"
+#include "utility.hpp"
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
+using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
+using namespace sdbusplus::xyz::openbmc_project::Power::Fault::Error;
 
 namespace witherspoon
 {
@@ -22,15 +35,56 @@
 namespace psu
 {
 
+
 void PowerSupply::analyze()
 {
-    //TODO analyze() needs to be implemented in this class.
+    using namespace witherspoon::pmbus;
+
+    try
+    {
+        auto curUVFault = pmbusIntf.readBit(VIN_UV_FAULT, Type::Hwmon);
+        //TODO: 3 consecutive reads should be performed.
+        // If 3 consecutive reads are seen, log the fault.
+        // Driver gives cached value, read once a second.
+        // increment for fault on, decrement for fault off, to deglitch.
+        // If count reaches 3, we have fault. If count reaches 0, fault is
+        // cleared.
+
+        //TODO: INPUT FAULT or WARNING bit to check from STATUS_WORD
+        // pmbus-core update to read high byte of STATUS_WORD?
+
+        if ((curUVFault != vinUVFault) || inputFault)
+        {
+            if (curUVFault)
+            {
+                //FIXME - metadata
+                report<PowerSupplyUnderVoltageFault>();
+                vinUVFault = true;
+            }
+            else
+            {
+                log<level::INFO>("VIN_UV_FAULT cleared");
+                vinUVFault = false;
+            }
+        }
+    }
+    catch (ReadFailure& e)
+    {
+        if (!readFailLogged)
+        {
+            commit<ReadFailure>();
+            readFailLogged = true;
+            // TODO - Need to reset that to false at start of power on, or
+            // presence change.
+        }
+    }
+
     return;
 }
 
 void PowerSupply::clearFaults()
 {
-    //TODO
+    //TODO - Clear faults at pre-poweron.
     return;
 }
 
diff --git a/power-supply/power_supply.hpp b/power-supply/power_supply.hpp
index 4249024..43489d2 100644
--- a/power-supply/power_supply.hpp
+++ b/power-supply/power_supply.hpp
@@ -1,5 +1,6 @@
 #pragma once
 #include "device.hpp"
+#include "pmbus.hpp"
 
 namespace witherspoon
 {
@@ -29,10 +30,14 @@
          * @param[in] inst - the device instance
          * @param[in] objpath - the path to monitor
          * @param[in] invpath - the inventory path to use
+         * @param[in] bus - D-Bus bus object
          */
         PowerSupply(const std::string& name, size_t inst,
-                    const std::string& objpath, const std::string& invpath)
-            : Device(name, inst), monitorPath(objpath), inventoryPath(invpath)
+                    const std::string& objpath,
+                    const std::string& invpath,
+                    sdbusplus::bus::bus& bus)
+            : Device(name, inst), monitorPath(objpath), inventoryPath(invpath),
+            bus(bus), pmbusIntf(objpath)
         {
         }
 
@@ -65,6 +70,39 @@
          * The D-Bus path to use for this power supply's inventory status.
          */
         std::string inventoryPath;
+
+        /** @brief Connection for sdbusplus bus */
+        sdbusplus::bus::bus& bus;
+
+        /**
+         * @brief Pointer to the PMBus interface
+         *
+         * Used to read out of or write to the /sysfs tree(s) containing files
+         * that a device driver monitors the PMBus interface to the power
+         * supplies.
+         */
+        witherspoon::pmbus::PMBus pmbusIntf;
+
+        /**
+         * @brief Has a PMBus read failure already been logged?
+         */
+        bool readFailLogged = false;
+
+        /**
+         * @brief Set to true when a VIN UV fault has been detected
+         *
+         * This is the VIN_UV_FAULT bit in the low byte from the STATUS_WORD
+         * command response.
+         */
+        bool vinUVFault = false;
+
+        /**
+         * @brief Set to true when an input fault or warning is detected
+         *
+         * This is the "INPUT FAULT OR WARNING" bit in the high byte from the
+         * STATUS_WORD command response.
+         */
+        bool inputFault = false;
 };
 
 }
diff --git a/xyz/openbmc_project/Power/Fault.errors.yaml b/xyz/openbmc_project/Power/Fault.errors.yaml
index 4b96049..bd9d7b5 100644
--- a/xyz/openbmc_project/Power/Fault.errors.yaml
+++ b/xyz/openbmc_project/Power/Fault.errors.yaml
@@ -1,3 +1,5 @@
+- name: PowerSupplyUnderVoltageFault
+  description: The power supply has indicated an input or under voltage fault condition.
 - 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 b9e41f6..0274523 100644
--- a/xyz/openbmc_project/Power/Fault.metadata.yaml
+++ b/xyz/openbmc_project/Power/Fault.metadata.yaml
@@ -1,3 +1,5 @@
+- name: PowerSupplyUnderVoltageFault
+  level: ERR
 - name: Shutdown
   level: ERR