regulators: Clear cached hardware data during boot

Clear cached data about hardware devices when the system is powering on
(booting).

While the system was powered off, hardware devices containing voltage
regulators could have been added, removed, or replaced.  Cached hardware
data might now be invalid.

Tested:
* Ran automated test cases
* Verified that cached data is cleared without errors during boot
  * When config file was found and loaded
  * When no config file was found

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: Ief45fe32ddcb122847d201e89ca1267526a87e3c
diff --git a/phosphor-regulators/docs/design.md b/phosphor-regulators/docs/design.md
index b99fe74..b4c154c 100644
--- a/phosphor-regulators/docs/design.md
+++ b/phosphor-regulators/docs/design.md
@@ -13,7 +13,8 @@
 
 The `phosphor-regulators` application is a single-threaded C++ executable.  It
 is a 'daemon' process that runs continually.  The application is launched by
-systemd when the system receives standby power.
+systemd when the BMC reaches the Ready state and before the chassis is powered
+on.
 
 The application is driven by a system-specific JSON configuration file.  The
 JSON file is found and parsed at runtime.  The parsing process creates a
diff --git a/phosphor-regulators/src/chassis.cpp b/phosphor-regulators/src/chassis.cpp
index 85e8275..f84540d 100644
--- a/phosphor-regulators/src/chassis.cpp
+++ b/phosphor-regulators/src/chassis.cpp
@@ -30,6 +30,15 @@
     }
 }
 
+void Chassis::clearCache()
+{
+    // Clear any cached data in each device
+    for (std::unique_ptr<Device>& device : devices)
+    {
+        device->clearCache();
+    }
+}
+
 void Chassis::closeDevices(Services& services)
 {
     // Log debug message in journal
diff --git a/phosphor-regulators/src/chassis.hpp b/phosphor-regulators/src/chassis.hpp
index 86d99f8..7462094 100644
--- a/phosphor-regulators/src/chassis.hpp
+++ b/phosphor-regulators/src/chassis.hpp
@@ -86,6 +86,11 @@
     void addToIDMap(IDMap& idMap);
 
     /**
+     * Clear any cached data about hardware devices.
+     */
+    void clearCache();
+
+    /**
      * Close the devices within this chassis, if any.
      *
      * @param services system services like error logging and the journal
diff --git a/phosphor-regulators/src/device.cpp b/phosphor-regulators/src/device.cpp
index 062ff27..1290077 100644
--- a/phosphor-regulators/src/device.cpp
+++ b/phosphor-regulators/src/device.cpp
@@ -37,6 +37,16 @@
     }
 }
 
+void Device::clearCache()
+{
+    // If presence detection is defined for this device
+    if (presenceDetection)
+    {
+        // Clear cached presence data
+        presenceDetection->clearCache();
+    }
+}
+
 void Device::close(Services& services)
 {
     try
diff --git a/phosphor-regulators/src/device.hpp b/phosphor-regulators/src/device.hpp
index ccdb30a..75ff904 100644
--- a/phosphor-regulators/src/device.hpp
+++ b/phosphor-regulators/src/device.hpp
@@ -87,6 +87,11 @@
     void addToIDMap(IDMap& idMap);
 
     /**
+     * Clear any cached data about hardware devices.
+     */
+    void clearCache();
+
+    /**
      * Closes this device.
      *
      * Closes any interfaces that are open to this device.  Releases any other
diff --git a/phosphor-regulators/src/manager.cpp b/phosphor-regulators/src/manager.cpp
index 51c8944..73e49bf 100644
--- a/phosphor-regulators/src/manager.cpp
+++ b/phosphor-regulators/src/manager.cpp
@@ -90,6 +90,9 @@
 
 void Manager::configure()
 {
+    // Clear any cached data or error history related to hardware devices
+    clearHardwareData();
+
     // Verify System object exists; this means config file has been loaded
     if (system)
     {
@@ -181,7 +184,7 @@
             // Close the regulator devices in the system.  Monitoring is
             // normally disabled because the system is being powered off.  The
             // devices should be closed in case hardware is removed or replaced
-            // while the system is at standby.
+            // while the system is powered off.
             system->closeDevices(services);
         }
     }
@@ -200,6 +203,21 @@
     // collect/update telemetry for each regulator
 }
 
+void Manager::clearHardwareData()
+{
+    // Clear any cached hardware presence data
+    services.getPresenceService().clearCache();
+
+    // Verify System object exists; this means config file has been loaded
+    if (system)
+    {
+        // Clear any cached hardware data in the System object
+        system->clearCache();
+    }
+
+    // TODO: Clear error history related to hardware devices
+}
+
 void Manager::findCompatibleSystemTypes()
 {
     using namespace phosphor::power::util;
diff --git a/phosphor-regulators/src/manager.hpp b/phosphor-regulators/src/manager.hpp
index 6c77fb7..f1aa0d9 100644
--- a/phosphor-regulators/src/manager.hpp
+++ b/phosphor-regulators/src/manager.hpp
@@ -94,6 +94,15 @@
 
   private:
     /**
+     * Clear any cached data or error history related to hardware devices.
+     *
+     * This method should be called when the system is powering on (booting).
+     * While the system was powered off, hardware could have been added,
+     * removed, or replaced.
+     */
+    void clearHardwareData();
+
+    /**
      * Finds the list of compatible system types using D-Bus methods.
      *
      * This list is used to find the correct JSON configuration file for the
diff --git a/phosphor-regulators/src/system.cpp b/phosphor-regulators/src/system.cpp
index a032935..3cc2eb3 100644
--- a/phosphor-regulators/src/system.cpp
+++ b/phosphor-regulators/src/system.cpp
@@ -34,6 +34,15 @@
     }
 }
 
+void System::clearCache()
+{
+    // Clear any cached data in each chassis
+    for (std::unique_ptr<Chassis>& oneChassis : chassis)
+    {
+        oneChassis->clearCache();
+    }
+}
+
 void System::closeDevices(Services& services)
 {
     // Close devices in each chassis
diff --git a/phosphor-regulators/src/system.hpp b/phosphor-regulators/src/system.hpp
index 2ef14b8..715cb4b 100644
--- a/phosphor-regulators/src/system.hpp
+++ b/phosphor-regulators/src/system.hpp
@@ -61,6 +61,11 @@
     }
 
     /**
+     * Clear any cached data about hardware devices.
+     */
+    void clearCache();
+
+    /**
      * Close the regulator devices in the system.
      *
      * @param services system services like error logging and the journal
diff --git a/phosphor-regulators/test/chassis_tests.cpp b/phosphor-regulators/test/chassis_tests.cpp
index 451e137..601bf3f 100644
--- a/phosphor-regulators/test/chassis_tests.cpp
+++ b/phosphor-regulators/test/chassis_tests.cpp
@@ -111,6 +111,47 @@
     EXPECT_THROW(idMap.getRail("rail3"), std::invalid_argument);
 }
 
+TEST(ChassisTests, ClearCache)
+{
+    // Create PresenceDetection
+    std::vector<std::unique_ptr<Action>> actions{};
+    std::unique_ptr<PresenceDetection> presenceDetection =
+        std::make_unique<PresenceDetection>(std::move(actions));
+    PresenceDetection* presenceDetectionPtr = presenceDetection.get();
+
+    // Create Device that contains PresenceDetection
+    std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
+    std::unique_ptr<Device> device = std::make_unique<Device>(
+        "reg1", true,
+        "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
+        std::move(i2cInterface), std::move(presenceDetection));
+    Device* devicePtr = device.get();
+
+    // Create Chassis that contains Device
+    std::vector<std::unique_ptr<Device>> devices{};
+    devices.emplace_back(std::move(device));
+    std::unique_ptr<Chassis> chassis =
+        std::make_unique<Chassis>(1, std::move(devices));
+    Chassis* chassisPtr = chassis.get();
+
+    // Create System that contains Chassis
+    std::vector<std::unique_ptr<Rule>> rules{};
+    std::vector<std::unique_ptr<Chassis>> chassisVec{};
+    chassisVec.emplace_back(std::move(chassis));
+    System system{std::move(rules), std::move(chassisVec)};
+
+    // Cache presence value in PresenceDetection
+    MockServices services{};
+    presenceDetectionPtr->execute(services, system, *chassisPtr, *devicePtr);
+    EXPECT_TRUE(presenceDetectionPtr->getCachedPresence().has_value());
+
+    // Clear cached data in Chassis
+    chassisPtr->clearCache();
+
+    // Verify presence value no longer cached in PresenceDetection
+    EXPECT_FALSE(presenceDetectionPtr->getCachedPresence().has_value());
+}
+
 TEST(ChassisTests, CloseDevices)
 {
     // Test where no devices were specified in constructor
diff --git a/phosphor-regulators/test/device_tests.cpp b/phosphor-regulators/test/device_tests.cpp
index ad2b9c2..717bb9d 100644
--- a/phosphor-regulators/test/device_tests.cpp
+++ b/phosphor-regulators/test/device_tests.cpp
@@ -151,6 +151,65 @@
     EXPECT_THROW(idMap.getRail("vdd2"), std::invalid_argument);
 }
 
+TEST(DeviceTests, ClearCache)
+{
+    // Test where Device does not contain a PresenceDetection object
+    try
+    {
+        Device device{
+            "vdd_reg", false,
+            "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
+            std::move(createI2CInterface())};
+        device.clearCache();
+    }
+    catch (...)
+    {
+        ADD_FAILURE() << "Should not have caught exception.";
+    }
+
+    // Test where Device contains a PresenceDetection object
+    {
+        // Create PresenceDetection
+        std::vector<std::unique_ptr<Action>> actions{};
+        std::unique_ptr<PresenceDetection> presenceDetection =
+            std::make_unique<PresenceDetection>(std::move(actions));
+        PresenceDetection* presenceDetectionPtr = presenceDetection.get();
+
+        // Create Device
+        std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
+        std::unique_ptr<Device> device = std::make_unique<Device>(
+            "reg1", true,
+            "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
+            std::move(i2cInterface), std::move(presenceDetection));
+        Device* devicePtr = device.get();
+
+        // Create Chassis that contains Device
+        std::vector<std::unique_ptr<Device>> devices{};
+        devices.emplace_back(std::move(device));
+        std::unique_ptr<Chassis> chassis =
+            std::make_unique<Chassis>(1, std::move(devices));
+        Chassis* chassisPtr = chassis.get();
+
+        // Create System that contains Chassis
+        std::vector<std::unique_ptr<Rule>> rules{};
+        std::vector<std::unique_ptr<Chassis>> chassisVec{};
+        chassisVec.emplace_back(std::move(chassis));
+        System system{std::move(rules), std::move(chassisVec)};
+
+        // Cache presence value in PresenceDetection
+        MockServices services{};
+        presenceDetectionPtr->execute(services, system, *chassisPtr,
+                                      *devicePtr);
+        EXPECT_TRUE(presenceDetectionPtr->getCachedPresence().has_value());
+
+        // Clear cached data in Device
+        devicePtr->clearCache();
+
+        // Verify presence value no longer cached in PresenceDetection
+        EXPECT_FALSE(presenceDetectionPtr->getCachedPresence().has_value());
+    }
+}
+
 TEST(DeviceTests, Close)
 {
     // Test where works: I2C interface is not open
diff --git a/phosphor-regulators/test/system_tests.cpp b/phosphor-regulators/test/system_tests.cpp
index d5b5b1a..b71155c 100644
--- a/phosphor-regulators/test/system_tests.cpp
+++ b/phosphor-regulators/test/system_tests.cpp
@@ -66,6 +66,47 @@
     EXPECT_EQ(system.getRules()[0]->getID(), "set_voltage_rule");
 }
 
+TEST(SystemTests, ClearCache)
+{
+    // Create PresenceDetection
+    std::vector<std::unique_ptr<Action>> actions{};
+    std::unique_ptr<PresenceDetection> presenceDetection =
+        std::make_unique<PresenceDetection>(std::move(actions));
+    PresenceDetection* presenceDetectionPtr = presenceDetection.get();
+
+    // Create Device that contains PresenceDetection
+    std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
+    std::unique_ptr<Device> device = std::make_unique<Device>(
+        "reg1", true,
+        "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
+        std::move(i2cInterface), std::move(presenceDetection));
+    Device* devicePtr = device.get();
+
+    // Create Chassis that contains Device
+    std::vector<std::unique_ptr<Device>> devices{};
+    devices.emplace_back(std::move(device));
+    std::unique_ptr<Chassis> chassis =
+        std::make_unique<Chassis>(1, std::move(devices));
+    Chassis* chassisPtr = chassis.get();
+
+    // Create System that contains Chassis
+    std::vector<std::unique_ptr<Rule>> rules{};
+    std::vector<std::unique_ptr<Chassis>> chassisVec{};
+    chassisVec.emplace_back(std::move(chassis));
+    System system{std::move(rules), std::move(chassisVec)};
+
+    // Cache presence value in PresenceDetection
+    MockServices services{};
+    presenceDetectionPtr->execute(services, system, *chassisPtr, *devicePtr);
+    EXPECT_TRUE(presenceDetectionPtr->getCachedPresence().has_value());
+
+    // Clear cached data in System
+    system.clearCache();
+
+    // Verify presence value no longer cached in PresenceDetection
+    EXPECT_FALSE(presenceDetectionPtr->getCachedPresence().has_value());
+}
+
 TEST(SystemTests, CloseDevices)
 {
     // Specify an empty rules vector