Implement processor throttle dbus properties

- create processor throttle dbus objects for each OCC (processor)
- update throttle properties based on OCC poll response data
  or safe mode status.

Throttle data will be made available via Redfish

NAME                                       TYPE      SIGNATURE RESULT/VALUE FLAGS
org.freedesktop.DBus.Introspectable        interface -         -            -
.Introspect                                method    -         s            -
org.freedesktop.DBus.Peer                  interface -         -            -
.GetMachineId                              method    -         s            -
.Ping                                      method    -         -            -
org.freedesktop.DBus.Properties            interface -         -            -
.Get                                       method    ss        v            -
.GetAll                                    method    s         a{sv}        -
.Set                                       method    ssv       -            -
.PropertiesChanged                         signal    sa{sv}as  -            -
xyz.openbmc_project.Control.Power.Throttle interface -         -            -
.ThrottleCauses                            property  as        0            emits-change
.Throttled                                 property  b         false        emits-change

Example of throttled processor (due to a power limit):
as 1 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.PowerLimit"

Change-Id: I0af9d82fab9d694427d0adaa45f4a372d25fbc12
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
diff --git a/occ_status.cpp b/occ_status.cpp
index 85b70ee..b89e469 100644
--- a/occ_status.cpp
+++ b/occ_status.cpp
@@ -18,6 +18,9 @@
 
 using namespace phosphor::logging;
 
+using ThrottleObj =
+    sdbusplus::xyz::openbmc_project::Control::Power::server::Throttle;
+
 // Handles updates to occActive property
 bool Status::occActive(bool value)
 {
@@ -28,6 +31,9 @@
                              .c_str());
         if (value)
         {
+            // Clear prior throttle reason (before setting device active)
+            updateThrottle(false, THROTTLED_ALL);
+
             // Set the device active
             device.setActive(true);
 
@@ -77,6 +83,9 @@
 
             // Set the device inactive
             device.setActive(false);
+
+            // Clear throttles (OCC not active after disabling device)
+            updateThrottle(false, THROTTLED_ALL);
         }
     }
     else if (value && !device.active())
@@ -555,5 +564,121 @@
     }
 }
 
+// Update processor throttle status on dbus
+void Status::updateThrottle(const bool isThrottled, const uint8_t newReason)
+{
+    if (!throttleHandle)
+    {
+        return;
+    }
+
+    uint8_t newThrottleCause = throttleCause;
+
+    if (isThrottled) // throttled due to newReason
+    {
+        if ((newReason & throttleCause) == 0)
+        {
+            // set the bit(s) for passed in reason
+            newThrottleCause |= newReason;
+        }
+        // else no change
+    }
+    else // no longer throttled due to newReason
+    {
+        if ((newReason & throttleCause) != 0)
+        {
+            // clear the bit(s) for passed in reason
+            newThrottleCause &= ~newReason;
+        }
+        // else no change
+    }
+
+    if (newThrottleCause != throttleCause)
+    {
+        if (newThrottleCause == THROTTLED_NONE)
+        {
+            log<level::DEBUG>(
+                fmt::format(
+                    "updateThrottle: OCC{} no longer throttled (prior reason: {})",
+                    instance, throttleCause)
+                    .c_str());
+            throttleCause = THROTTLED_NONE;
+            throttleHandle->throttled(false);
+            throttleHandle->throttleCauses({});
+        }
+        else
+        {
+            log<level::DEBUG>(
+                fmt::format(
+                    "updateThrottle: OCC{} is throttled with reason {} (prior reason: {})",
+                    instance, newThrottleCause, throttleCause)
+                    .c_str());
+            throttleCause = newThrottleCause;
+
+            std::vector<ThrottleObj::ThrottleReasons> updatedCauses;
+            if (throttleCause & THROTTLED_POWER)
+            {
+                updatedCauses.push_back(
+                    throttleHandle->ThrottleReasons::PowerLimit);
+            }
+            if (throttleCause & THROTTLED_THERMAL)
+            {
+                updatedCauses.push_back(
+                    throttleHandle->ThrottleReasons::ThermalLimit);
+            }
+            if (throttleCause & THROTTLED_SAFE)
+            {
+                updatedCauses.push_back(
+                    throttleHandle->ThrottleReasons::ManagementDetectedFault);
+            }
+            throttleHandle->throttleCauses(updatedCauses);
+            throttleHandle->throttled(true);
+        }
+    }
+    // else no change to throttle status
+}
+
+// Get processor path associated with this OCC
+void Status::readProcAssociation()
+{
+    std::string managingPath = path + "/power_managing";
+    log<level::DEBUG>(
+        fmt::format("readProcAssociation: getting endpoints for {} ({})",
+                    managingPath, path)
+            .c_str());
+    try
+    {
+        utils::PropertyValue procPathProperty{};
+        procPathProperty = utils::getProperty(
+            managingPath, "xyz.openbmc_project.Association", "endpoints");
+        auto result = std::get<std::vector<std::string>>(procPathProperty);
+        if (result.size() > 0)
+        {
+            procPath = result[0];
+            log<level::INFO>(
+                fmt::format("readProcAssociation: OCC{} has proc={}", instance,
+                            procPath.c_str())
+                    .c_str());
+        }
+        else
+        {
+            log<level::ERR>(
+                fmt::format(
+                    "readProcAssociation: No processor associated with OCC{} / {}",
+                    instance, path)
+                    .c_str());
+        }
+    }
+    catch (const sdbusplus::exception_t& e)
+    {
+        log<level::ERR>(
+            fmt::format(
+                "readProcAssociation: Unable to get proc assocated with {} - {}",
+                path, e.what())
+                .c_str());
+        procPath = {};
+    }
+}
+
 } // namespace occ
 } // namespace open_power