blob: 9745682bc9ac8ad5486346bfaa284e132b8ef17b [file] [log] [blame]
#pragma once
#include "occ_pass_through.hpp"
#include "occ_status.hpp"
#ifdef PLDM
#include "pldm.hpp"
#include <libphal.H>
#endif
#include "powercap.hpp"
#include "utils.hpp"
#ifdef POWER10
#include "powermode.hpp"
#endif
#include <sdbusplus/bus.hpp>
#include <sdeventplus/event.hpp>
#include <sdeventplus/utility/timer.hpp>
#include <cstring>
#include <functional>
#include <vector>
namespace sdbusRule = sdbusplus::bus::match::rules;
namespace open_power
{
namespace occ
{
#ifdef READ_OCC_SENSORS
enum occFruType
{
processorCore = 0,
internalMemCtlr = 1,
dimm = 2,
memCtrlAndDimm = 3,
VRMVdd = 6,
PMIC = 7,
memCtlrExSensor = 8,
processorIoRing = 9
};
#endif
/** @brief Default time, in seconds, between OCC poll commands */
#ifndef POWER10
constexpr unsigned int defaultPollingInterval = 1;
#else
constexpr unsigned int defaultPollingInterval = 5;
#endif
constexpr auto AMBIENT_PATH =
"/xyz/openbmc_project/sensors/temperature/Ambient_Virtual_Temp";
constexpr auto AMBIENT_INTERFACE = "xyz.openbmc_project.Sensor.Value";
constexpr auto AMBIENT_PROP = "Value";
constexpr auto ALTITUDE_PATH = "/xyz/openbmc_project/sensors/altitude/Altitude";
constexpr auto ALTITUDE_INTERFACE = "xyz.openbmc_project.Sensor.Value";
constexpr auto ALTITUDE_PROP = "Value";
/** @class Manager
* @brief Builds and manages OCC objects
*/
struct Manager
{
public:
Manager() = delete;
Manager(const Manager&) = delete;
Manager& operator=(const Manager&) = delete;
Manager(Manager&&) = delete;
Manager& operator=(Manager&&) = delete;
~Manager() = default;
/** @brief Adds OCC pass-through and status objects on the bus
* when corresponding CPU inventory is created.
*
* @param[in] event - Unique ptr reference to sd_event
*/
explicit Manager(EventPtr& event) :
event(event), pollInterval(defaultPollingInterval),
sdpEvent(sdeventplus::Event::get_default()),
_pollTimer(
std::make_unique<
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
sdpEvent, std::bind(&Manager::pollerTimerExpired, this))),
ambientPropChanged(
utils::getBus(),
sdbusRule::member("PropertiesChanged") +
sdbusRule::path(AMBIENT_PATH) +
sdbusRule::argN(0, AMBIENT_INTERFACE) +
sdbusRule::interface("org.freedesktop.DBus.Properties"),
std::bind(&Manager::ambientCallback, this, std::placeholders::_1))
#ifdef PLDM
,
pldmHandle(std::make_unique<pldm::Interface>(
std::bind(std::mem_fn(&Manager::updateOCCActive), this,
std::placeholders::_1, std::placeholders::_2),
std::bind(std::mem_fn(&Manager::sbeHRESETResult), this,
std::placeholders::_1, std::placeholders::_2),
std::bind(std::mem_fn(&Manager::updateOccSafeMode), this,
std::placeholders::_1),
event))
#endif
#ifdef POWER10
,
discoverTimer(
std::make_unique<
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
sdpEvent, std::bind(&Manager::findAndCreateObjects, this))),
waitForAllOccsTimer(
std::make_unique<
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
sdpEvent, std::bind(&Manager::occsNotAllRunning, this)))
#ifdef PLDM
,
throttlePldmTraceTimer(
std::make_unique<
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
sdpEvent, std::bind(&Manager::throttlePldmTraceExpired, this)))
#endif
#endif // POWER10
{
#ifdef I2C_OCC
// I2C OCC status objects are initialized directly
initStatusObjects();
#else
findAndCreateObjects();
#endif
readAltitude();
}
/** @brief Return the number of bound OCCs */
inline auto getNumOCCs() const
{
return activeCount;
}
#ifdef PLDM
/** @brief Called by a Device to report that the SBE timed out
* and appropriate action should be taken
*
* @param[in] instance - the OCC instance id
*/
void sbeTimeout(unsigned int instance);
#endif
/** @brief Return the latest ambient and altitude readings
*
* @param[out] ambientValid - true if ambientTemp is valid
* @param[out] ambient - ambient temperature in degrees C
* @param[out] altitude - altitude in meters
*/
void getAmbientData(bool& ambientValid, uint8_t& ambientTemp,
uint16_t& altitude) const;
/** @brief Notify pcap object to update bounds */
void updatePcapBounds() const;
/**
* @brief Set all sensor values of this OCC to NaN.
* @param[in] id - Id of the OCC.
* */
void setSensorValueToNaN(uint32_t id) const;
/** @brief Set all sensor values of this OCC to NaN and non functional.
*
* @param[in] id - Id of the OCC.
*/
void setSensorValueToNonFunctional(uint32_t id) const;
private:
/** @brief Creates the OCC D-Bus objects.
*/
void findAndCreateObjects();
/** @brief Callback that responds to cpu creation in the inventory -
* by creating the needed objects.
*
* @param[in] msg - bus message
*
* @returns 0 to indicate success
*/
int cpuCreated(sdbusplus::message_t& msg);
/** @brief Create child OCC objects.
*
* @param[in] occ - the occ name, such as occ0.
*/
void createObjects(const std::string& occ);
/** @brief Callback handler invoked by Status object when the OccActive
* property is changed. This is needed to make sure that the
* error detection is started only after all the OCCs are bound.
* Similarly, when one of the OCC gets its OccActive property
* un-set, then the OCC error detection needs to be stopped on
* all the OCCs
*
* @param[in] status - OccActive status
*/
void statusCallBack(instanceID instance, bool status);
/** @brief Sends a Heartbeat command to host control command handler */
void sendHeartBeat();
/** @brief reference to sd_event wrapped in unique_ptr */
EventPtr& event;
/** @brief OCC pass-through objects */
std::vector<std::unique_ptr<PassThrough>> passThroughObjects;
/** @brief OCC Status objects */
std::vector<std::unique_ptr<Status>> statusObjects;
/** @brief Power cap monitor and occ notification object */
std::unique_ptr<open_power::occ::powercap::PowerCap> pcap;
#ifdef POWER10
/** @brief Power mode monitor and notification object */
std::unique_ptr<open_power::occ::powermode::PowerMode> pmode;
#endif
/** @brief sbdbusplus match objects */
std::vector<sdbusplus::bus::match_t> cpuMatches;
/** @brief Number of OCCs that are bound */
uint8_t activeCount = 0;
/** @brief Number of seconds between poll commands */
uint8_t pollInterval;
/** @brief Ambient temperature of the system in degrees C */
uint8_t ambient = 0xFF; // default: not available
/** @brief Altitude of the system in meters */
uint16_t altitude = 0xFFFF; // default: not available
/** @brief Poll timer event */
sdeventplus::Event sdpEvent;
/** @brief Flags to indicate if waiting for all of the OCC active sensors to
* come online */
bool waitingForAllOccActiveSensors = false;
/** @brief Set containing intance numbers of any OCCs that became active
* while waiting for status objects to be created */
std::set<uint8_t> queuedActiveState;
/**
* @brief The timer to be used once the OCC goes active. When it expires,
* a POLL command will be sent to the OCC and then timer restarted.
*/
std::unique_ptr<
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
_pollTimer;
/** @brief Subscribe to ambient temperature changed events */
sdbusplus::bus::match_t ambientPropChanged;
#ifdef I2C_OCC
/** @brief Init Status objects for I2C OCC devices
*
* It iterates in /sys/bus/i2c/devices, finds all occ hwmon devices
* and creates status objects.
*/
void initStatusObjects();
#endif
#ifdef PLDM
/** @brief Callback handler invoked by the PLDM event handler when state of
* the OCC is toggled by the host. The caller passes the instance
* of the OCC and state of the OCC.
*
* @param[in] instance - instance of the OCC
* @param[in] status - true when the OCC goes active and false when the OCC
* goes inactive
*
* @return true if setting the state of OCC is successful and false if it
* fails.
*/
bool updateOCCActive(instanceID instance, bool status);
/** @brief Callback handler invoked by the PLDM event handler when mode of
* the OCC SAFE MODE is inacted or cleared.
*/
void updateOccSafeMode(bool safeState);
/** @brief Callback handler invoked by PLDM sensor change when
* the HRESET succeeds or fails.
*
* @param[in] instance - the SBE instance id
* @param[in] success - true if the HRESET succeeded, otherwise false
*/
void sbeHRESETResult(instanceID instance, bool success);
/** @brief Helper function to check whether an SBE dump should be collected
* now.
*
* @param[in] instance - the SBE instance id
*
* @return true if an SBE dump should be collected and false if not
*/
bool sbeCanDump(unsigned int instance);
/** @brief Helper function to set the SBE state through PDBG/PHAL
*
* @param[in] instance - instance of the SBE
* @param[in] state - the state to which the SBE should be set
*
*/
void setSBEState(unsigned int instance, enum sbe_state state);
/** @brief Helper function to get the SBE instance PDBG processor target
*
* @param[in] instance - the SBE instance id
*
* @return a pointer to the PDBG target
*/
struct pdbg_target* getPdbgTarget(unsigned int instance);
/** @brief Whether pdbg_targets_init has been called */
bool pdbgInitialized = false;
std::unique_ptr<pldm::Interface> pldmHandle = nullptr;
#endif
#ifdef POWER10
/**
* @brief Timer used when discovering OCCs in /dev.
*/
std::unique_ptr<
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
discoverTimer;
/**
* @brief Used when discovering /dev/occ objects to know if
* any were added since the last check.
*/
std::vector<int> prevOCCSearch;
/**
* @brief Timer used when waiting for OCCs to go active.
*/
std::unique_ptr<
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
waitForAllOccsTimer;
#ifdef PLDM
/**
* @brief Timer used to throttle PLDM traces when there are problems
determining the OCC status via pldm. Used to prevent excessive
journal traces.
*/
std::unique_ptr<
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
throttlePldmTraceTimer;
/**
* @brief onPldmTimeoutCreatePel flag will be used to indicate if
* a PEL should get created when the throttlePldmTraceTimer expires.
* The first time the throttlePldmTraceTimer expires, the traces
* will be throttled and then the timer gets restarted. The
* next time the timer expires, a PEL will get created.
*/
bool onPldmTimeoutCreatePel = false;
/** @brief Check if all of the OCC Active sensors are available and if not
* restart the discoverTimer
*/
void throttlePldmTraceExpired();
/** @brief Create a PEL when the code is not able to obtain the OCC PDRs
* via PLDM. This is called when the throttlePldmTraceTimer expires.
*/
void createPldmSensorPEL();
#endif
/** @brief Called when code times out waiting for all OCCs to be running or
* after the app is restarted (Status does not callback into
* Manager).
*/
void occsNotAllRunning();
/** @brief Check if all of the OCC Active sensors are available and if not
* restart the discoverTimer
*/
void checkAllActiveSensors();
#endif // POWER10
/**
* @brief Called when poll timer expires and forces a POLL command to the
* OCC. The poll timer will then be restarted.
* */
void pollerTimerExpired();
/**
* @brief Finds the OCC devices in /dev
*
* @return The IDs of the OCCs - 0, 1, etc.
*/
std::vector<int> findOCCsInDev();
#ifdef READ_OCC_SENSORS
/**
* @brief Gets the occ sensor values.
* @param[in] occ - pointer to OCCs Status object
* */
void getSensorValues(std::unique_ptr<Status>& occ);
/**
* @brief Trigger OCC driver to read the temperature sensors.
* @param[in] path - path of the OCC sensors.
* @param[in] id - Id of the OCC.
* */
void readTempSensors(const fs::path& path, uint32_t id);
/**
* @brief Trigger OCC driver to read the power sensors.
* @param[in] path - path of the OCC sensors.
* @param[in] id - Id of the OCC.
* */
void readPowerSensors(const fs::path& path, uint32_t id);
/** @brief Store the existing OCC sensors on D-BUS */
std::map<std::string, uint32_t> existingSensors;
/** @brief Get FunctionID from the `powerX_label` file.
* @param[in] value - the value of the `powerX_label` file.
* @returns FunctionID of the power sensors.
*/
std::optional<std::string>
getPowerLabelFunctionID(const std::string& value);
/** @brief The power sensor names map */
const std::map<std::string, std::string> powerSensorName = {
{"system", "total_power"}, {"1", "p0_mem_power"},
{"2", "p1_mem_power"}, {"3", "p2_mem_power"},
{"4", "p3_mem_power"}, {"5", "p0_power"},
{"6", "p1_power"}, {"7", "p2_power"},
{"8", "p3_power"}, {"9", "p0_cache_power"},
{"10", "p1_cache_power"}, {"11", "p2_cache_power"},
{"12", "p3_cache_power"}, {"13", "io_a_power"},
{"14", "io_b_power"}, {"15", "io_c_power"},
{"16", "fans_a_power"}, {"17", "fans_b_power"},
{"18", "storage_a_power"}, {"19", "storage_b_power"},
{"23", "mem_cache_power"}, {"25", "p0_mem_0_power"},
{"26", "p0_mem_1_power"}, {"27", "p0_mem_2_power"},
{"35", "pcie_dcm0_power"}, {"36", "pcie_dcm1_power"},
{"37", "pcie_dcm2_power"}, {"38", "pcie_dcm3_power"},
{"39", "io_dcm0_power"}, {"40", "io_dcm1_power"},
{"41", "io_dcm2_power"}, {"42", "io_dcm3_power"},
{"43", "avdd_total_power"}};
/** @brief The dimm temperature sensor names map */
const std::map<uint32_t, std::string> dimmTempSensorName = {
{internalMemCtlr, "_intmb_temp"},
{dimm, "_dram_temp"},
{memCtrlAndDimm, "_dram_extmb_temp"},
{PMIC, "_pmic_temp"},
{memCtlrExSensor, "_extmb_temp"}};
/** @brief The dimm DVFS temperature sensor names map */
const std::map<uint32_t, std::string> dimmDVFSSensorName = {
{internalMemCtlr, "dimm_intmb_dvfs_temp"},
{dimm, "dimm_dram_dvfs_temp"},
{memCtrlAndDimm, "dimm_dram_extmb_dvfs_temp"},
{PMIC, "dimm_pmic_dvfs_temp"},
{memCtlrExSensor, "dimm_extmb_dvfs_temp"}};
#endif
/** @brief Read the altitude from DBus */
void readAltitude();
/** @brief Callback function when ambient temperature changes
*
* @param[in] msg - Data associated with subscribed signal
*/
void ambientCallback(sdbusplus::message_t& msg);
/** @brief Confirm that a single OCC master was found and start presence
* monitoring
*/
void validateOccMaster();
};
} // namespace occ
} // namespace open_power