blob: a07c2726d07b2a5a05db29883ae8b36a7e4a410d [file] [log] [blame]
#pragma once
#include "config.h"
#include "i2c_occ.hpp"
#include "occ_command.hpp"
#include "occ_device.hpp"
#include "occ_events.hpp"
#include "powercap.hpp"
#include "powermode.hpp"
#include "utils.hpp"
#include <org/open_power/Control/Host/server.hpp>
#include <org/open_power/OCC/Status/server.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/server/object.hpp>
#ifdef POWER10
#include <sdeventplus/event.hpp>
#include <sdeventplus/utility/timer.hpp>
#endif
#include <xyz/openbmc_project/Control/Power/Throttle/server.hpp>
#include <functional>
namespace open_power
{
namespace occ
{
class Manager;
namespace Base = sdbusplus::org::open_power::OCC::server;
using Interface = sdbusplus::server::object_t<Base::Status>;
namespace xyzBase = sdbusplus::xyz::openbmc_project::Control::Power::server;
using ThrottleInterface = sdbusplus::server::object_t<xyzBase::Throttle>;
// IPMID's host control application
namespace Control = sdbusplus::org::open_power::Control::server;
// For waiting on signals
namespace sdbusRule = sdbusplus::bus::match::rules;
// OCC status instance. Ex. for "occ0", the instance is 0
using instanceID = unsigned int;
// IPMI sensor ID for a given OCC instance
using sensorID = uint8_t;
// Human readable sensor name for DBus tree. E.g. "CPU0_OCC"
using sensorName = std::string;
// OCC sensors definitions in the map
using sensorDefs = std::tuple<sensorID, sensorName>;
// OCC sysfs name prefix
const std::string sysfsName = "occ-hwmon";
const uint8_t THROTTLED_NONE = 0x00;
const uint8_t THROTTLED_POWER = 0x01;
const uint8_t THROTTLED_THERMAL = 0x02;
const uint8_t THROTTLED_SAFE = 0x04;
const uint8_t THROTTLED_ALL = 0xFF;
/** @class Status
* @brief Implementation of OCC Active Status
*/
class Status : public Interface
{
public:
Status() = delete;
~Status() = default;
Status(const Status&) = delete;
Status& operator=(const Status&) = delete;
Status(Status&&) = default;
Status& operator=(Status&&) = default;
/** @brief Constructs the Status object and
* the underlying device object
*
* @param[in] event - sd_event unique pointer reference
* @param[in] path - DBus object path
* @param[in] manager - OCC manager instance
* @param[in] callBack - Callback handler to invoke during
* property change
* @param[in] resetCallBack - callback handler to invoke for resetting the
* OCC if PLDM is the host communication
* protocol
*/
Status(EventPtr& event, const char* path, Manager& managerRef,
#ifdef POWER10
std::unique_ptr<powermode::PowerMode>& powerModeRef,
#endif
std::function<void(instanceID, bool)> callBack = nullptr
#ifdef PLDM
,
std::function<void(instanceID)> resetCallBack = nullptr
#endif
) :
Interface(utils::getBus(), getDbusPath(path).c_str(),
Interface::action::defer_emit),
path(path), managerCallBack(callBack), instance(getInstance(path)),
manager(managerRef),
#ifdef POWER10
pmode(powerModeRef),
#endif
device(event,
#ifdef I2C_OCC
fs::path(DEV_PATH) / i2c_occ::getI2cDeviceName(path),
#else
fs::path(DEV_PATH) /
fs::path(sysfsName + "." + std::to_string(instance + 1)),
#endif
managerRef, *this,
#ifdef POWER10
powerModeRef,
#endif
instance),
hostControlSignal(
utils::getBus(),
sdbusRule::type::signal() + sdbusRule::member("CommandComplete") +
sdbusRule::path("/org/open_power/control/host0") +
sdbusRule::interface("org.open_power.Control.Host") +
sdbusRule::argN(0, Control::convertForMessage(
Control::Host::Command::OCCReset)),
std::bind(std::mem_fn(&Status::hostControlEvent), this,
std::placeholders::_1)),
occCmd(instance, (fs::path(OCC_CONTROL_ROOT) /
(std::string(OCC_NAME) + std::to_string(instance)))
.c_str())
#ifdef POWER10
,
sdpEvent(sdeventplus::Event::get_default()),
safeStateDelayTimer(
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>(
sdpEvent, std::bind(&Status::safeStateDelayExpired, this))),
occReadStateFailTimer(
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>(
sdpEvent, std::bind(&Status::occReadStateNow, this)))
#endif
#ifdef PLDM
,
resetCallBack(resetCallBack)
#endif
{
// Announce that we are ready
this->emit_object_added();
}
/** @brief Since we are overriding the setter-occActive but not the
* getter-occActive, we need to have this using in order to
* allow passthrough usage of the getter-occActive
*/
using Base::Status::occActive;
/** @brief SET OccActive to True or False
*
* @param[in] value - Intended value
*
* @return - Updated value of the property
*/
bool occActive(bool value) override;
/** @brief Starts OCC error detection */
inline void addErrorWatch()
{
return device.addErrorWatch();
}
/** @brief Stops OCC error detection */
inline void removeErrorWatch()
{
return device.removeErrorWatch();
}
/** @brief Starts to watch how many OCCs are present on the master */
inline void addPresenceWatchMaster()
{
return device.addPresenceWatchMaster();
}
/** @brief Gets the occ instance number */
unsigned int getOccInstanceID()
{
return instance;
}
/** @brief Is this OCC the master OCC */
bool isMasterOcc()
{
return device.master();
}
/** @brief Read OCC state (will trigger kernel to poll the OCC) */
void readOccState();
/** @brief Called when device errors are detected
*
* @param[in] d - description of the error that occurred
*/
void deviceError(Error::Descriptor d = Error::Descriptor());
#ifdef POWER10
/** @brief Handle additional tasks when the OCCs reach active state */
void occsWentActive();
/** @brief Send Ambient & Altitude data to OCC
*
* @param[in] ambient - temperature to send (0xFF will force read
* of current temperature and altitude)
* @param[in] altitude - altitude to send (0xFFFF = unavailable)
*
* @return SUCCESS on success
*/
CmdStatus sendAmbient(const uint8_t ambient = 0xFF,
const uint16_t altitude = 0xFFFF);
/** @brief Set flag indicating if PLDM sensor has been received
*
* @param[in] wasReceived - true if PLDM sensor was read
*/
void setPldmSensorReceived(const bool wasReceived)
{
pldmSensorStateReceived = wasReceived;
}
/** @brief Read flag indicating if PLDM sensor has been read
*
* @return true if sensor has been read
*/
bool getPldmSensorReceived()
{
return pldmSensorStateReceived;
}
#endif // POWER10
/** @brief Return the HWMON path for this OCC
*
* @return path or empty path if not found
*/
fs::path getHwmonPath();
/** @brief Update the processor path associated with this OCC
*/
void updateProcAssociation()
{
readProcAssociation();
if (nullptr != throttleHandle)
{
throttleHandle.reset();
}
if (!procPath.empty())
{
throttleHandle = std::make_unique<ThrottleInterface>(
utils::getBus(), procPath.c_str());
}
}
/** @brief Update the processor throttle status on dbus
*/
void updateThrottle(const bool isThrottled, const uint8_t reason);
private:
/** @brief OCC dbus object path */
std::string path;
/** @brief Processor path associated with this OCC */
std::string procPath;
/** @brief Callback handler to be invoked during property change.
* This is a handler in Manager class
*/
std::function<void(instanceID, bool)> managerCallBack;
/** @brief OCC instance number. Ex, 0,1, etc */
unsigned int instance;
/** @brief The last state read from the OCC */
unsigned int lastState = 0;
/** @brief Number of retry attempts to open file and update state. */
const unsigned int occReadRetries = 1;
/** @brief Current number of retries attempted towards occReadRetries. */
size_t currentOccReadRetriesCount = 0;
/** @brief The Trigger to indicate OCC State is valid or not. */
bool stateValid = false;
/** @brief OCC instance to Sensor definitions mapping */
static const std::map<instanceID, sensorDefs> sensorMap;
/** @brief OCC manager object */
const Manager& manager;
#ifdef POWER10
/** @brief OCC PowerMode object */
std::unique_ptr<powermode::PowerMode>& pmode;
#endif
/** @brief OCC device object to do bind and unbind */
Device device;
/** @brief Subscribe to host control signal
*
* Once the OCC reset is requested, BMC sends that message to host.
* If the host does not ack the message, then there would be a timeout
* and we need to catch that to log an error
**/
sdbusplus::bus::match_t hostControlSignal;
/** @brief Command object to send commands to the OCC */
OccCommand occCmd;
#ifdef POWER10
/** @brief timer event */
sdeventplus::Event sdpEvent;
#endif
/** @brief hwmon path for this OCC */
fs::path hwmonPath;
/** @brief flag indicating if the OCC sensor has been received */
bool pldmSensorStateReceived = false;
/** @brief Callback function on host control signals
*
* @param[in] msg - Data associated with subscribed signal
*/
void hostControlEvent(sdbusplus::message_t& msg);
/** @brief Sends a message to host control command handler to reset OCC
*/
void resetOCC();
/** @brief Determines the instance ID by specified object path.
* @param[in] path Estimated OCC Dbus object path
* @return Instance number
*/
static int getInstance(const std::string& path)
{
return (path.empty() ? 0 : path.back() - '0');
}
#ifdef POWER10
/**
* @brief Timer that is started when OCC is detected to be in safe mode
*/
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>
safeStateDelayTimer;
/** @brief Callback for timer that is started when OCC was detected to be in
* safe mode. Called to verify and then disable and reset the OCCs.
*/
void safeStateDelayExpired();
/**
* @brief Timer that is started when OCC read Valid state failed.
*/
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>
occReadStateFailTimer;
#endif // POWER10
/** @brief Callback for timer that is started when OCC state
* was not able to be read. Called to attempt another read when needed.
*/
void occReadStateNow();
/** @brief Override the sensor name with name from the definition.
* @param[in] estimatedPath - Estimated OCC Dbus object path
* @return Fixed OCC DBus object path
*/
static std::string getDbusPath(const std::string& estimatedPath)
{
if (!estimatedPath.empty())
{
auto it = sensorMap.find(getInstance(estimatedPath));
if (sensorMap.end() != it)
{
auto& name = std::get<1>(it->second);
if (!name.empty() && name != "None")
{
auto objectPath = fs::path(estimatedPath);
objectPath.replace_filename(name);
return objectPath.string();
}
}
}
return estimatedPath;
}
#ifdef PLDM
std::function<void(instanceID)> resetCallBack = nullptr;
#endif
/** @brief Current throttle reason(s) for this processor */
uint8_t throttleCause = THROTTLED_NONE;
/** @brief Throttle interface for the processor associated with this OCC */
std::unique_ptr<ThrottleInterface> throttleHandle;
/** @brief Read the processor path associated with this OCC */
void readProcAssociation();
};
} // namespace occ
} // namespace open_power