116a2ced3SJonathan Doman // Copyright (c) 2022 Intel Corporation 216a2ced3SJonathan Doman // 316a2ced3SJonathan Doman // Licensed under the Apache License, Version 2.0 (the "License"); 416a2ced3SJonathan Doman // you may not use this file except in compliance with the License. 516a2ced3SJonathan Doman // You may obtain a copy of the License at 616a2ced3SJonathan Doman // 716a2ced3SJonathan Doman // http://www.apache.org/licenses/LICENSE-2.0 816a2ced3SJonathan Doman // 916a2ced3SJonathan Doman // Unless required by applicable law or agreed to in writing, software 1016a2ced3SJonathan Doman // distributed under the License is distributed on an "AS IS" BASIS, 1116a2ced3SJonathan Doman // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1216a2ced3SJonathan Doman // See the License for the specific language governing permissions and 1316a2ced3SJonathan Doman // limitations under the License. 1416a2ced3SJonathan Doman 1516a2ced3SJonathan Doman #include "cpuinfo_utils.hpp" 1616a2ced3SJonathan Doman #include "speed_select.hpp" 1716a2ced3SJonathan Doman 1816a2ced3SJonathan Doman #include <iostream> 1916a2ced3SJonathan Doman 2016a2ced3SJonathan Doman namespace cpu_info 2116a2ced3SJonathan Doman { 2216a2ced3SJonathan Doman namespace sst 2316a2ced3SJonathan Doman { 2416a2ced3SJonathan Doman 2516a2ced3SJonathan Doman /** 2616a2ced3SJonathan Doman * Convenience RAII object for Wake-On-PECI (WOP) management, since PECI Config 2716a2ced3SJonathan Doman * Local accesses to the OS Mailbox require the package to pop up to PC2. Also 2816a2ced3SJonathan Doman * provides PCode OS Mailbox routine. 2916a2ced3SJonathan Doman * 300fe13abaSManojkiran Eda * Since multiple applications may be modifying WOP, we'll use this algorithm: 3116a2ced3SJonathan Doman * Whenever a PECI command fails with associated error code, set WOP bit and 3216a2ced3SJonathan Doman * retry command. Upon manager destruction, clear WOP bit only if we previously 3316a2ced3SJonathan Doman * set it. 3416a2ced3SJonathan Doman */ 3516a2ced3SJonathan Doman struct PECIManager 3616a2ced3SJonathan Doman { 3716a2ced3SJonathan Doman uint8_t peciAddress; 3816a2ced3SJonathan Doman bool peciWoken; 3916a2ced3SJonathan Doman CPUModel cpuModel; 4016a2ced3SJonathan Doman uint8_t mbBus; 41b5d7222fSJonathan Doman WakePolicy wakePolicy; 4216a2ced3SJonathan Doman PECIManagercpu_info::sst::PECIManager43b5d7222fSJonathan Doman PECIManager(uint8_t address, CPUModel model, WakePolicy wakePolicy_) : 44b5d7222fSJonathan Doman peciAddress(address), peciWoken(false), cpuModel(model), 45b5d7222fSJonathan Doman wakePolicy(wakePolicy_) 4616a2ced3SJonathan Doman { 47b86e4f19SJason M. Bills mbBus = (model == iceLake) ? mbBusIceLake : mbBusOther; 4816a2ced3SJonathan Doman } 4916a2ced3SJonathan Doman ~PECIManagercpu_info::sst::PECIManager5016a2ced3SJonathan Doman ~PECIManager() 5116a2ced3SJonathan Doman { 5216a2ced3SJonathan Doman // If we're being destroyed due to a PECIError, try to clear the mode 5316a2ced3SJonathan Doman // bit, but catch and ignore any duplicate error it might raise to 5416a2ced3SJonathan Doman // prevent termination. 5516a2ced3SJonathan Doman try 5616a2ced3SJonathan Doman { 5716a2ced3SJonathan Doman if (peciWoken) 5816a2ced3SJonathan Doman { 5916a2ced3SJonathan Doman setWakeOnPECI(false); 6016a2ced3SJonathan Doman } 6116a2ced3SJonathan Doman } 6216a2ced3SJonathan Doman catch (const PECIError& err) 6316a2ced3SJonathan Doman {} 6416a2ced3SJonathan Doman } 6516a2ced3SJonathan Doman isSleepingcpu_info::sst::PECIManager6616a2ced3SJonathan Doman static bool isSleeping(EPECIStatus libStatus, uint8_t completionCode) 6716a2ced3SJonathan Doman { 6816a2ced3SJonathan Doman // PECI completion code defined in peci-ioctl.h which is not available 6916a2ced3SJonathan Doman // for us to include. 7016a2ced3SJonathan Doman constexpr int PECI_DEV_CC_UNAVAIL_RESOURCE = 0x82; 7116a2ced3SJonathan Doman // Observed library returning DRIVER_ERR for reads and TIMEOUT for 7216a2ced3SJonathan Doman // writes while PECI is sleeping. Either way, the completion code from 7316a2ced3SJonathan Doman // PECI client should be reliable indicator of need to set WOP. 7416a2ced3SJonathan Doman return libStatus != PECI_CC_SUCCESS && 7516a2ced3SJonathan Doman completionCode == PECI_DEV_CC_UNAVAIL_RESOURCE; 7616a2ced3SJonathan Doman } 7716a2ced3SJonathan Doman 7816a2ced3SJonathan Doman /** 7916a2ced3SJonathan Doman * Send a single PECI PCS write to modify the Wake-On-PECI mode bit 8016a2ced3SJonathan Doman */ setWakeOnPECIcpu_info::sst::PECIManager8116a2ced3SJonathan Doman void setWakeOnPECI(bool enable) 8216a2ced3SJonathan Doman { 8316a2ced3SJonathan Doman uint8_t completionCode; 84*1d73dcccSPatrick Williams EPECIStatus libStatus = 85*1d73dcccSPatrick Williams peci_WrPkgConfig(peciAddress, 5, enable ? 1 : 0, 0, 86*1d73dcccSPatrick Williams sizeof(uint32_t), &completionCode); 8716a2ced3SJonathan Doman if (!checkPECIStatus(libStatus, completionCode)) 8816a2ced3SJonathan Doman { 8916a2ced3SJonathan Doman throw PECIError("Failed to set Wake-On-PECI mode bit"); 9016a2ced3SJonathan Doman } 9116a2ced3SJonathan Doman 9216a2ced3SJonathan Doman if (enable) 9316a2ced3SJonathan Doman { 9416a2ced3SJonathan Doman peciWoken = true; 9516a2ced3SJonathan Doman } 9616a2ced3SJonathan Doman } 9716a2ced3SJonathan Doman 9816a2ced3SJonathan Doman // PCode OS Mailbox interface register locations 99b86e4f19SJason M. Bills static constexpr int mbBusIceLake = 14; 10016a2ced3SJonathan Doman static constexpr int mbBusOther = 31; 10116a2ced3SJonathan Doman static constexpr int mbSegment = 0; 10216a2ced3SJonathan Doman static constexpr int mbDevice = 30; 10316a2ced3SJonathan Doman static constexpr int mbFunction = 1; 10416a2ced3SJonathan Doman static constexpr int mbDataReg = 0xA0; 10516a2ced3SJonathan Doman static constexpr int mbInterfaceReg = 0xA4; 10616a2ced3SJonathan Doman static constexpr int mbRegSize = sizeof(uint32_t); 10716a2ced3SJonathan Doman 10816a2ced3SJonathan Doman enum class MailboxStatus 10916a2ced3SJonathan Doman { 11016a2ced3SJonathan Doman NoError = 0x0, 11116a2ced3SJonathan Doman InvalidCommand = 0x1, 11216a2ced3SJonathan Doman IllegalData = 0x16 11316a2ced3SJonathan Doman }; 11416a2ced3SJonathan Doman 11516a2ced3SJonathan Doman /** 11616a2ced3SJonathan Doman * Send a single Write PCI Config Local command, targeting the PCU CR1 11716a2ced3SJonathan Doman * register block. 11816a2ced3SJonathan Doman * 11916a2ced3SJonathan Doman * @param[in] regAddress PCI Offset of register. 12016a2ced3SJonathan Doman * @param[in] data Data to write. 12116a2ced3SJonathan Doman */ wrMailboxRegcpu_info::sst::PECIManager12216a2ced3SJonathan Doman void wrMailboxReg(uint16_t regAddress, uint32_t data) 12316a2ced3SJonathan Doman { 12416a2ced3SJonathan Doman uint8_t completionCode; 125b5d7222fSJonathan Doman bool tryWaking = (wakePolicy == wakeAllowed); 12616a2ced3SJonathan Doman while (true) 12716a2ced3SJonathan Doman { 12816a2ced3SJonathan Doman EPECIStatus libStatus = peci_WrEndPointPCIConfigLocal( 12916a2ced3SJonathan Doman peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress, 13016a2ced3SJonathan Doman mbRegSize, data, &completionCode); 13116a2ced3SJonathan Doman if (tryWaking && isSleeping(libStatus, completionCode)) 13216a2ced3SJonathan Doman { 13316a2ced3SJonathan Doman setWakeOnPECI(true); 13416a2ced3SJonathan Doman tryWaking = false; 13516a2ced3SJonathan Doman continue; 13616a2ced3SJonathan Doman } 13716a2ced3SJonathan Doman else if (!checkPECIStatus(libStatus, completionCode)) 13816a2ced3SJonathan Doman { 13916a2ced3SJonathan Doman throw PECIError("Failed to write mailbox reg"); 14016a2ced3SJonathan Doman } 14116a2ced3SJonathan Doman break; 14216a2ced3SJonathan Doman } 14316a2ced3SJonathan Doman } 14416a2ced3SJonathan Doman 14516a2ced3SJonathan Doman /** 14616a2ced3SJonathan Doman * Send a single Read PCI Config Local command, targeting the PCU CR1 14716a2ced3SJonathan Doman * register block. 14816a2ced3SJonathan Doman * 14916a2ced3SJonathan Doman * @param[in] regAddress PCI offset of register. 15016a2ced3SJonathan Doman * 15116a2ced3SJonathan Doman * @return Register value 15216a2ced3SJonathan Doman */ rdMailboxRegcpu_info::sst::PECIManager15316a2ced3SJonathan Doman uint32_t rdMailboxReg(uint16_t regAddress) 15416a2ced3SJonathan Doman { 15516a2ced3SJonathan Doman uint8_t completionCode; 15616a2ced3SJonathan Doman uint32_t outputData; 157b5d7222fSJonathan Doman bool tryWaking = (wakePolicy == wakeAllowed); 15816a2ced3SJonathan Doman while (true) 15916a2ced3SJonathan Doman { 16016a2ced3SJonathan Doman EPECIStatus libStatus = peci_RdEndPointConfigPciLocal( 16116a2ced3SJonathan Doman peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress, 16216a2ced3SJonathan Doman mbRegSize, reinterpret_cast<uint8_t*>(&outputData), 16316a2ced3SJonathan Doman &completionCode); 16416a2ced3SJonathan Doman if (tryWaking && isSleeping(libStatus, completionCode)) 16516a2ced3SJonathan Doman { 16616a2ced3SJonathan Doman setWakeOnPECI(true); 16716a2ced3SJonathan Doman tryWaking = false; 16816a2ced3SJonathan Doman continue; 16916a2ced3SJonathan Doman } 17016a2ced3SJonathan Doman if (!checkPECIStatus(libStatus, completionCode)) 17116a2ced3SJonathan Doman { 17216a2ced3SJonathan Doman throw PECIError("Failed to read mailbox reg"); 17316a2ced3SJonathan Doman } 17416a2ced3SJonathan Doman break; 17516a2ced3SJonathan Doman } 17616a2ced3SJonathan Doman return outputData; 17716a2ced3SJonathan Doman } 17816a2ced3SJonathan Doman 17916a2ced3SJonathan Doman /** 18016a2ced3SJonathan Doman * Send command on PCode OS Mailbox interface. 18116a2ced3SJonathan Doman * 18216a2ced3SJonathan Doman * @param[in] command Main command ID. 18316a2ced3SJonathan Doman * @param[in] subCommand Sub command ID. 18416a2ced3SJonathan Doman * @param[in] inputData Data to put in mailbox. Is always written, but 18516a2ced3SJonathan Doman * will be ignored by PCode if command is a 18616a2ced3SJonathan Doman * "getter". 18716a2ced3SJonathan Doman * @param[out] responseCode Optional parameter to receive the 18816a2ced3SJonathan Doman * mailbox-level response status. If null, a 18916a2ced3SJonathan Doman * PECIError will be thrown for error status. 19016a2ced3SJonathan Doman * 19116a2ced3SJonathan Doman * @return Data returned in mailbox. Value is undefined if command is a 19216a2ced3SJonathan Doman * "setter". 19316a2ced3SJonathan Doman */ sendPECIOSMailboxCmdcpu_info::sst::PECIManager19416a2ced3SJonathan Doman uint32_t sendPECIOSMailboxCmd(uint8_t command, uint8_t subCommand, 19516a2ced3SJonathan Doman uint32_t inputData = 0, 19616a2ced3SJonathan Doman MailboxStatus* responseCode = nullptr) 19716a2ced3SJonathan Doman { 19816a2ced3SJonathan Doman // The simple mailbox algorithm just says to wait until the busy bit 19916a2ced3SJonathan Doman // is clear, but we'll give up after 10 tries. It's arbitrary but that's 20016a2ced3SJonathan Doman // quite long wall clock time. 20116a2ced3SJonathan Doman constexpr int mbRetries = 10; 20216a2ced3SJonathan Doman constexpr uint32_t mbBusyBit = bit(31); 20316a2ced3SJonathan Doman 20416a2ced3SJonathan Doman // Wait until RUN_BUSY == 0 20516a2ced3SJonathan Doman int attempts = mbRetries; 20616a2ced3SJonathan Doman while ((rdMailboxReg(mbInterfaceReg) & mbBusyBit) != 0 && 20716a2ced3SJonathan Doman --attempts > 0) 20816a2ced3SJonathan Doman ; 20916a2ced3SJonathan Doman if (attempts == 0) 21016a2ced3SJonathan Doman { 21116a2ced3SJonathan Doman throw PECIError("OS Mailbox failed to become free"); 21216a2ced3SJonathan Doman } 21316a2ced3SJonathan Doman 21416a2ced3SJonathan Doman // Write required command specific input data to data register 21516a2ced3SJonathan Doman wrMailboxReg(mbDataReg, inputData); 21616a2ced3SJonathan Doman 21716a2ced3SJonathan Doman // Write required command specific command/sub-command values and set 21816a2ced3SJonathan Doman // RUN_BUSY bit in interface register. 21916a2ced3SJonathan Doman uint32_t interfaceReg = 22016a2ced3SJonathan Doman mbBusyBit | (static_cast<uint32_t>(subCommand) << 8) | command; 22116a2ced3SJonathan Doman wrMailboxReg(mbInterfaceReg, interfaceReg); 22216a2ced3SJonathan Doman 22316a2ced3SJonathan Doman // Wait until RUN_BUSY == 0 22416a2ced3SJonathan Doman attempts = mbRetries; 22516a2ced3SJonathan Doman do 22616a2ced3SJonathan Doman { 22716a2ced3SJonathan Doman interfaceReg = rdMailboxReg(mbInterfaceReg); 22816a2ced3SJonathan Doman } while ((interfaceReg & mbBusyBit) != 0 && --attempts > 0); 22916a2ced3SJonathan Doman if (attempts == 0) 23016a2ced3SJonathan Doman { 23116a2ced3SJonathan Doman throw PECIError("OS Mailbox failed to return"); 23216a2ced3SJonathan Doman } 23316a2ced3SJonathan Doman 23416a2ced3SJonathan Doman // Read command return status or error code from interface register 23516a2ced3SJonathan Doman auto status = static_cast<MailboxStatus>(interfaceReg & 0xFF); 23616a2ced3SJonathan Doman if (responseCode != nullptr) 23716a2ced3SJonathan Doman { 23816a2ced3SJonathan Doman *responseCode = status; 23916a2ced3SJonathan Doman } 24016a2ced3SJonathan Doman else if (status != MailboxStatus::NoError) 24116a2ced3SJonathan Doman { 24216a2ced3SJonathan Doman throw PECIError(std::string("OS Mailbox returned with error: ") + 24316a2ced3SJonathan Doman std::to_string(static_cast<int>(status))); 24416a2ced3SJonathan Doman } 24516a2ced3SJonathan Doman 24616a2ced3SJonathan Doman // Read command return data from the data register 24716a2ced3SJonathan Doman return rdMailboxReg(mbDataReg); 24816a2ced3SJonathan Doman } 24916a2ced3SJonathan Doman }; 25016a2ced3SJonathan Doman 25116a2ced3SJonathan Doman /** 25216a2ced3SJonathan Doman * Base class for set of PECI OS Mailbox commands. 25316a2ced3SJonathan Doman * Constructing it runs the command and stores the value for use by derived 25416a2ced3SJonathan Doman * class accessor methods. 25516a2ced3SJonathan Doman */ 25616a2ced3SJonathan Doman template <uint8_t subcommand> 25716a2ced3SJonathan Doman struct OsMailboxCommand 25816a2ced3SJonathan Doman { 25916a2ced3SJonathan Doman enum ErrorPolicy 26016a2ced3SJonathan Doman { 26116a2ced3SJonathan Doman Throw, 26216a2ced3SJonathan Doman NoThrow 26316a2ced3SJonathan Doman }; 26416a2ced3SJonathan Doman 26516a2ced3SJonathan Doman uint32_t value; 266e513c9c3SJonathan Doman PECIManager::MailboxStatus status = PECIManager::MailboxStatus::NoError; 26716a2ced3SJonathan Doman /** 26816a2ced3SJonathan Doman * Construct the command object with required PECI address and up to 4 26916a2ced3SJonathan Doman * optional 1-byte input data parameters. 27016a2ced3SJonathan Doman */ OsMailboxCommandcpu_info::sst::OsMailboxCommand27116a2ced3SJonathan Doman OsMailboxCommand(PECIManager& pm, uint8_t param1 = 0, uint8_t param2 = 0, 27216a2ced3SJonathan Doman uint8_t param3 = 0, uint8_t param4 = 0) : 27316a2ced3SJonathan Doman OsMailboxCommand(pm, ErrorPolicy::Throw, param1, param2, param3, param4) 27416a2ced3SJonathan Doman {} 27516a2ced3SJonathan Doman OsMailboxCommandcpu_info::sst::OsMailboxCommand27616a2ced3SJonathan Doman OsMailboxCommand(PECIManager& pm, ErrorPolicy errorPolicy, 27716a2ced3SJonathan Doman uint8_t param1 = 0, uint8_t param2 = 0, uint8_t param3 = 0, 27816a2ced3SJonathan Doman uint8_t param4 = 0) 27916a2ced3SJonathan Doman { 28016a2ced3SJonathan Doman DEBUG_PRINT << "Running OS Mailbox command " 28116a2ced3SJonathan Doman << static_cast<int>(subcommand) << '\n'; 282*1d73dcccSPatrick Williams PECIManager::MailboxStatus* callStatus = 283*1d73dcccSPatrick Williams errorPolicy == Throw ? nullptr : &status; 28416a2ced3SJonathan Doman uint32_t param = (static_cast<uint32_t>(param4) << 24) | 28516a2ced3SJonathan Doman (static_cast<uint32_t>(param3) << 16) | 28616a2ced3SJonathan Doman (static_cast<uint32_t>(param2) << 8) | param1; 28716a2ced3SJonathan Doman value = pm.sendPECIOSMailboxCmd(0x7F, subcommand, param, callStatus); 28816a2ced3SJonathan Doman } 28916a2ced3SJonathan Doman 29016a2ced3SJonathan Doman /** Return whether the mailbox status indicated success or not. */ successcpu_info::sst::OsMailboxCommand29116a2ced3SJonathan Doman bool success() const 29216a2ced3SJonathan Doman { 29316a2ced3SJonathan Doman return status == PECIManager::MailboxStatus::NoError; 29416a2ced3SJonathan Doman } 29516a2ced3SJonathan Doman }; 29616a2ced3SJonathan Doman 29716a2ced3SJonathan Doman /** 29816a2ced3SJonathan Doman * Macro to define a derived class accessor method. 29916a2ced3SJonathan Doman * 30016a2ced3SJonathan Doman * @param[in] type Return type of accessor method. 30116a2ced3SJonathan Doman * @param[in] name Name of accessor method. 30216a2ced3SJonathan Doman * @param[in] hibit Most significant bit of field to access. 30316a2ced3SJonathan Doman * @param[in] lobit Least significant bit of field to access. 30416a2ced3SJonathan Doman */ 30516a2ced3SJonathan Doman #define FIELD(type, name, hibit, lobit) \ 30616a2ced3SJonathan Doman type name() const \ 30716a2ced3SJonathan Doman { \ 30816a2ced3SJonathan Doman return (value >> lobit) & (bit(hibit - lobit + 1) - 1); \ 30916a2ced3SJonathan Doman } 31016a2ced3SJonathan Doman 31116a2ced3SJonathan Doman struct GetLevelsInfo : OsMailboxCommand<0x0> 31216a2ced3SJonathan Doman { 31316a2ced3SJonathan Doman using OsMailboxCommand::OsMailboxCommand; 31416a2ced3SJonathan Doman FIELD(bool, enabled, 31, 31) 31516a2ced3SJonathan Doman FIELD(bool, lock, 24, 24) 31616a2ced3SJonathan Doman FIELD(unsigned, currentConfigTdpLevel, 23, 16) 31716a2ced3SJonathan Doman FIELD(unsigned, configTdpLevels, 15, 8) 31816a2ced3SJonathan Doman FIELD(unsigned, version, 7, 0) 31916a2ced3SJonathan Doman }; 32016a2ced3SJonathan Doman 32116a2ced3SJonathan Doman struct GetConfigTdpControl : OsMailboxCommand<0x1> 32216a2ced3SJonathan Doman { 32316a2ced3SJonathan Doman using OsMailboxCommand::OsMailboxCommand; 32416a2ced3SJonathan Doman FIELD(bool, pbfEnabled, 17, 17); 32516a2ced3SJonathan Doman FIELD(bool, factEnabled, 16, 16); 32616a2ced3SJonathan Doman FIELD(bool, pbfSupport, 1, 1); 32716a2ced3SJonathan Doman FIELD(bool, factSupport, 0, 0); 32816a2ced3SJonathan Doman }; 32916a2ced3SJonathan Doman 33016a2ced3SJonathan Doman struct SetConfigTdpControl : OsMailboxCommand<0x2> 33116a2ced3SJonathan Doman { 33216a2ced3SJonathan Doman using OsMailboxCommand::OsMailboxCommand; 33316a2ced3SJonathan Doman }; 33416a2ced3SJonathan Doman 33516a2ced3SJonathan Doman struct GetTdpInfo : OsMailboxCommand<0x3> 33616a2ced3SJonathan Doman { 33716a2ced3SJonathan Doman using OsMailboxCommand::OsMailboxCommand; 33816a2ced3SJonathan Doman FIELD(unsigned, tdpRatio, 23, 16); 33916a2ced3SJonathan Doman FIELD(unsigned, pkgTdp, 14, 0); 34016a2ced3SJonathan Doman }; 34116a2ced3SJonathan Doman 34216a2ced3SJonathan Doman struct GetCoreMask : OsMailboxCommand<0x6> 34316a2ced3SJonathan Doman { 34416a2ced3SJonathan Doman using OsMailboxCommand::OsMailboxCommand; 34516a2ced3SJonathan Doman FIELD(uint32_t, coresMask, 31, 0); 34616a2ced3SJonathan Doman }; 34716a2ced3SJonathan Doman 34816a2ced3SJonathan Doman struct GetTurboLimitRatios : OsMailboxCommand<0x7> 34916a2ced3SJonathan Doman { 35016a2ced3SJonathan Doman using OsMailboxCommand::OsMailboxCommand; 35116a2ced3SJonathan Doman }; 35216a2ced3SJonathan Doman 35316a2ced3SJonathan Doman struct SetLevel : OsMailboxCommand<0x8> 35416a2ced3SJonathan Doman { 35516a2ced3SJonathan Doman using OsMailboxCommand::OsMailboxCommand; 35616a2ced3SJonathan Doman }; 35716a2ced3SJonathan Doman 35816a2ced3SJonathan Doman struct GetRatioInfo : OsMailboxCommand<0xC> 35916a2ced3SJonathan Doman { 36016a2ced3SJonathan Doman using OsMailboxCommand::OsMailboxCommand; 36116a2ced3SJonathan Doman FIELD(unsigned, pm, 31, 24); 36216a2ced3SJonathan Doman FIELD(unsigned, pn, 23, 16); 36316a2ced3SJonathan Doman FIELD(unsigned, p1, 15, 8); 36416a2ced3SJonathan Doman FIELD(unsigned, p0, 7, 0); 36516a2ced3SJonathan Doman }; 36616a2ced3SJonathan Doman 36716a2ced3SJonathan Doman struct GetTjmaxInfo : OsMailboxCommand<0x5> 36816a2ced3SJonathan Doman { 36916a2ced3SJonathan Doman using OsMailboxCommand::OsMailboxCommand; 37016a2ced3SJonathan Doman FIELD(unsigned, tProchot, 7, 0); 37116a2ced3SJonathan Doman }; 37216a2ced3SJonathan Doman 37316a2ced3SJonathan Doman struct PbfGetCoreMaskInfo : OsMailboxCommand<0x20> 37416a2ced3SJonathan Doman { 37516a2ced3SJonathan Doman using OsMailboxCommand::OsMailboxCommand; 37616a2ced3SJonathan Doman FIELD(uint32_t, p1HiCoreMask, 31, 0); 37716a2ced3SJonathan Doman }; 37816a2ced3SJonathan Doman 37916a2ced3SJonathan Doman struct PbfGetP1HiP1LoInfo : OsMailboxCommand<0x21> 38016a2ced3SJonathan Doman { 38116a2ced3SJonathan Doman using OsMailboxCommand::OsMailboxCommand; 38216a2ced3SJonathan Doman FIELD(unsigned, p1Hi, 15, 8); 38316a2ced3SJonathan Doman FIELD(unsigned, p1Lo, 7, 0); 38416a2ced3SJonathan Doman }; 38516a2ced3SJonathan Doman 38616a2ced3SJonathan Doman /** 38716a2ced3SJonathan Doman * Implementation of SSTInterface based on OS Mailbox interface supported on ICX 38816a2ced3SJonathan Doman * and SPR processors. 38916a2ced3SJonathan Doman * It's expected that an instance of this class will be created for each 39016a2ced3SJonathan Doman * "atomic" set of operations. 39116a2ced3SJonathan Doman */ 39216a2ced3SJonathan Doman class SSTMailbox : public SSTInterface 39316a2ced3SJonathan Doman { 39416a2ced3SJonathan Doman private: 39516a2ced3SJonathan Doman uint8_t address; 39616a2ced3SJonathan Doman CPUModel model; 39716a2ced3SJonathan Doman PECIManager pm; 39816a2ced3SJonathan Doman 39916a2ced3SJonathan Doman static constexpr int mhzPerRatio = 100; 40016a2ced3SJonathan Doman 40116a2ced3SJonathan Doman public: SSTMailbox(uint8_t _address,CPUModel _model,WakePolicy wakePolicy)402b5d7222fSJonathan Doman SSTMailbox(uint8_t _address, CPUModel _model, WakePolicy wakePolicy) : 40316a2ced3SJonathan Doman address(_address), model(_model), 404b5d7222fSJonathan Doman pm(static_cast<uint8_t>(address), model, wakePolicy) 40516a2ced3SJonathan Doman {} ~SSTMailbox()406c39d3dfcSPatrick Williams ~SSTMailbox() {} 40716a2ced3SJonathan Doman ready()40816a2ced3SJonathan Doman bool ready() override 40916a2ced3SJonathan Doman { 41016a2ced3SJonathan Doman return true; 41116a2ced3SJonathan Doman } 41216a2ced3SJonathan Doman supportsControl()41316a2ced3SJonathan Doman bool supportsControl() override 41416a2ced3SJonathan Doman { 415949f634dSJonathan Doman switch (model) 416949f634dSJonathan Doman { 417b86e4f19SJason M. Bills case sapphireRapids: 418b86e4f19SJason M. Bills case emeraldRapids: 419949f634dSJonathan Doman return true; 420949f634dSJonathan Doman default: 421949f634dSJonathan Doman return false; 422949f634dSJonathan Doman } 42316a2ced3SJonathan Doman } 42416a2ced3SJonathan Doman currentLevel()42516a2ced3SJonathan Doman unsigned int currentLevel() override 42616a2ced3SJonathan Doman { 42716a2ced3SJonathan Doman return GetLevelsInfo(pm).currentConfigTdpLevel(); 42816a2ced3SJonathan Doman } maxLevel()429b4c3bcd7SJonathan Doman unsigned int maxLevel() override 43016a2ced3SJonathan Doman { 43116a2ced3SJonathan Doman return GetLevelsInfo(pm).configTdpLevels(); 43216a2ced3SJonathan Doman } ppEnabled()43316a2ced3SJonathan Doman bool ppEnabled() override 43416a2ced3SJonathan Doman { 43516a2ced3SJonathan Doman return GetLevelsInfo(pm).enabled(); 43616a2ced3SJonathan Doman } 43716a2ced3SJonathan Doman levelSupported(unsigned int level)43816a2ced3SJonathan Doman bool levelSupported(unsigned int level) override 43916a2ced3SJonathan Doman { 44016a2ced3SJonathan Doman GetConfigTdpControl tdpControl( 44116a2ced3SJonathan Doman pm, GetConfigTdpControl::ErrorPolicy::NoThrow, 44216a2ced3SJonathan Doman static_cast<uint8_t>(level)); 44316a2ced3SJonathan Doman return tdpControl.success(); 44416a2ced3SJonathan Doman } bfSupported(unsigned int level)44516a2ced3SJonathan Doman bool bfSupported(unsigned int level) override 44616a2ced3SJonathan Doman { 44716a2ced3SJonathan Doman return GetConfigTdpControl(pm, static_cast<uint8_t>(level)) 44816a2ced3SJonathan Doman .pbfSupport(); 44916a2ced3SJonathan Doman } tfSupported(unsigned int level)45016a2ced3SJonathan Doman bool tfSupported(unsigned int level) override 45116a2ced3SJonathan Doman { 45216a2ced3SJonathan Doman return GetConfigTdpControl(pm, static_cast<uint8_t>(level)) 45316a2ced3SJonathan Doman .factSupport(); 45416a2ced3SJonathan Doman } bfEnabled(unsigned int level)45516a2ced3SJonathan Doman bool bfEnabled(unsigned int level) override 45616a2ced3SJonathan Doman { 45716a2ced3SJonathan Doman return GetConfigTdpControl(pm, static_cast<uint8_t>(level)) 45816a2ced3SJonathan Doman .pbfEnabled(); 45916a2ced3SJonathan Doman } tfEnabled(unsigned int level)46016a2ced3SJonathan Doman bool tfEnabled(unsigned int level) override 46116a2ced3SJonathan Doman { 46216a2ced3SJonathan Doman return GetConfigTdpControl(pm, static_cast<uint8_t>(level)) 46316a2ced3SJonathan Doman .factEnabled(); 46416a2ced3SJonathan Doman } tdp(unsigned int level)46516a2ced3SJonathan Doman unsigned int tdp(unsigned int level) override 46616a2ced3SJonathan Doman { 46716a2ced3SJonathan Doman return GetTdpInfo(pm, static_cast<uint8_t>(level)).pkgTdp(); 46816a2ced3SJonathan Doman } coreCount(unsigned int level)46916a2ced3SJonathan Doman unsigned int coreCount(unsigned int level) override 47016a2ced3SJonathan Doman { 47116a2ced3SJonathan Doman return enabledCoreList(level).size(); 47216a2ced3SJonathan Doman } enabledCoreList(unsigned int level)47316a2ced3SJonathan Doman std::vector<unsigned int> enabledCoreList(unsigned int level) override 47416a2ced3SJonathan Doman { 47516a2ced3SJonathan Doman uint64_t coreMaskLo = 47616a2ced3SJonathan Doman GetCoreMask(pm, static_cast<uint8_t>(level), 0).coresMask(); 47716a2ced3SJonathan Doman uint64_t coreMaskHi = 47816a2ced3SJonathan Doman GetCoreMask(pm, static_cast<uint8_t>(level), 1).coresMask(); 47916a2ced3SJonathan Doman std::bitset<64> coreMask = (coreMaskHi << 32 | coreMaskLo); 48016a2ced3SJonathan Doman return convertMaskToList(coreMask); 48116a2ced3SJonathan Doman } sseTurboProfile(unsigned int level)48216a2ced3SJonathan Doman std::vector<TurboEntry> sseTurboProfile(unsigned int level) override 48316a2ced3SJonathan Doman { 48416a2ced3SJonathan Doman // Read the Turbo Ratio Limit Cores MSR which is used to generate the 48516a2ced3SJonathan Doman // Turbo Profile for each profile. This is a package scope MSR, so just 48616a2ced3SJonathan Doman // read thread 0. 48716a2ced3SJonathan Doman uint64_t trlCores; 48816a2ced3SJonathan Doman uint8_t cc; 48916a2ced3SJonathan Doman EPECIStatus status = peci_RdIAMSR(static_cast<uint8_t>(address), 0, 49016a2ced3SJonathan Doman 0x1AE, &trlCores, &cc); 49116a2ced3SJonathan Doman if (!checkPECIStatus(status, cc)) 49216a2ced3SJonathan Doman { 49316a2ced3SJonathan Doman throw PECIError("Failed to read TRL MSR"); 49416a2ced3SJonathan Doman } 49516a2ced3SJonathan Doman 49616a2ced3SJonathan Doman std::vector<TurboEntry> turboSpeeds; 49716a2ced3SJonathan Doman uint64_t limitRatioLo = 49816a2ced3SJonathan Doman GetTurboLimitRatios(pm, static_cast<uint8_t>(level), 0, 0).value; 49916a2ced3SJonathan Doman uint64_t limitRatioHi = 50016a2ced3SJonathan Doman GetTurboLimitRatios(pm, static_cast<uint8_t>(level), 1, 0).value; 50116a2ced3SJonathan Doman uint64_t limitRatios = (limitRatioHi << 32) | limitRatioLo; 50216a2ced3SJonathan Doman 50316a2ced3SJonathan Doman constexpr int maxTFBuckets = 8; 50416a2ced3SJonathan Doman for (int i = 0; i < maxTFBuckets; ++i) 50516a2ced3SJonathan Doman { 50616a2ced3SJonathan Doman size_t bucketCount = trlCores & 0xFF; 50716a2ced3SJonathan Doman int bucketSpeed = limitRatios & 0xFF; 50816a2ced3SJonathan Doman if (bucketCount != 0 && bucketSpeed != 0) 50916a2ced3SJonathan Doman { 51016a2ced3SJonathan Doman turboSpeeds.push_back({bucketSpeed * mhzPerRatio, bucketCount}); 51116a2ced3SJonathan Doman } 51216a2ced3SJonathan Doman 51316a2ced3SJonathan Doman trlCores >>= 8; 51416a2ced3SJonathan Doman limitRatios >>= 8; 51516a2ced3SJonathan Doman } 51616a2ced3SJonathan Doman return turboSpeeds; 51716a2ced3SJonathan Doman } p1Freq(unsigned int level)51816a2ced3SJonathan Doman unsigned int p1Freq(unsigned int level) override 51916a2ced3SJonathan Doman { 52016a2ced3SJonathan Doman return GetRatioInfo(pm, static_cast<uint8_t>(level)).p1() * mhzPerRatio; 52116a2ced3SJonathan Doman } p0Freq(unsigned int level)52216a2ced3SJonathan Doman unsigned int p0Freq(unsigned int level) override 52316a2ced3SJonathan Doman { 52416a2ced3SJonathan Doman return GetRatioInfo(pm, static_cast<uint8_t>(level)).p0() * mhzPerRatio; 52516a2ced3SJonathan Doman } prochotTemp(unsigned int level)52616a2ced3SJonathan Doman unsigned int prochotTemp(unsigned int level) override 52716a2ced3SJonathan Doman { 52816a2ced3SJonathan Doman return GetTjmaxInfo(pm, static_cast<uint8_t>(level)).tProchot(); 52916a2ced3SJonathan Doman } 53016a2ced3SJonathan Doman std::vector<unsigned int> bfHighPriorityCoreList(unsigned int level)53116a2ced3SJonathan Doman bfHighPriorityCoreList(unsigned int level) override 53216a2ced3SJonathan Doman { 533*1d73dcccSPatrick Williams uint64_t coreMaskLo = 534*1d73dcccSPatrick Williams PbfGetCoreMaskInfo(pm, static_cast<uint8_t>(level), 0) 53516a2ced3SJonathan Doman .p1HiCoreMask(); 536*1d73dcccSPatrick Williams uint64_t coreMaskHi = 537*1d73dcccSPatrick Williams PbfGetCoreMaskInfo(pm, static_cast<uint8_t>(level), 1) 53816a2ced3SJonathan Doman .p1HiCoreMask(); 53916a2ced3SJonathan Doman std::bitset<64> hiFreqCoreList = (coreMaskHi << 32) | coreMaskLo; 54016a2ced3SJonathan Doman return convertMaskToList(hiFreqCoreList); 54116a2ced3SJonathan Doman } bfHighPriorityFreq(unsigned int level)54216a2ced3SJonathan Doman unsigned int bfHighPriorityFreq(unsigned int level) override 54316a2ced3SJonathan Doman { 54416a2ced3SJonathan Doman return PbfGetP1HiP1LoInfo(pm, static_cast<uint8_t>(level)).p1Hi() * 54516a2ced3SJonathan Doman mhzPerRatio; 54616a2ced3SJonathan Doman } bfLowPriorityFreq(unsigned int level)54716a2ced3SJonathan Doman unsigned int bfLowPriorityFreq(unsigned int level) override 54816a2ced3SJonathan Doman { 54916a2ced3SJonathan Doman return PbfGetP1HiP1LoInfo(pm, static_cast<uint8_t>(level)).p1Lo() * 55016a2ced3SJonathan Doman mhzPerRatio; 55116a2ced3SJonathan Doman } 55216a2ced3SJonathan Doman setBfEnabled(bool enable)55316a2ced3SJonathan Doman void setBfEnabled(bool enable) override 55416a2ced3SJonathan Doman { 55516a2ced3SJonathan Doman GetConfigTdpControl getTDPControl(pm); 55616a2ced3SJonathan Doman bool tfEnabled = false; 55716a2ced3SJonathan Doman uint8_t param = (enable ? bit(1) : 0) | (tfEnabled ? bit(0) : 0); 55816a2ced3SJonathan Doman SetConfigTdpControl(pm, 0, 0, param); 55916a2ced3SJonathan Doman } setTfEnabled(bool enable)56016a2ced3SJonathan Doman void setTfEnabled(bool enable) override 56116a2ced3SJonathan Doman { 56216a2ced3SJonathan Doman // TODO: use cached BF value 56316a2ced3SJonathan Doman bool bfEnabled = false; 56416a2ced3SJonathan Doman uint8_t param = (bfEnabled ? bit(1) : 0) | (enable ? bit(0) : 0); 56516a2ced3SJonathan Doman SetConfigTdpControl(pm, 0, 0, param); 56616a2ced3SJonathan Doman } setCurrentLevel(unsigned int level)56716a2ced3SJonathan Doman void setCurrentLevel(unsigned int level) override 56816a2ced3SJonathan Doman { 56916a2ced3SJonathan Doman SetLevel(pm, static_cast<uint8_t>(level)); 57016a2ced3SJonathan Doman } 57116a2ced3SJonathan Doman }; 57216a2ced3SJonathan Doman 573b5d7222fSJonathan Doman static std::unique_ptr<SSTInterface> createMailbox(uint8_t address,CPUModel model,WakePolicy wakePolicy)574b5d7222fSJonathan Doman createMailbox(uint8_t address, CPUModel model, WakePolicy wakePolicy) 57516a2ced3SJonathan Doman { 57616a2ced3SJonathan Doman DEBUG_PRINT << "createMailbox\n"; 577949f634dSJonathan Doman switch (model) 57816a2ced3SJonathan Doman { 579b86e4f19SJason M. Bills case iceLake: 580b86e4f19SJason M. Bills case iceLakeD: 581b86e4f19SJason M. Bills case sapphireRapids: 582b86e4f19SJason M. Bills case emeraldRapids: 583b5d7222fSJonathan Doman return std::make_unique<SSTMailbox>(address, model, wakePolicy); 584949f634dSJonathan Doman default: 58516a2ced3SJonathan Doman return nullptr; 58616a2ced3SJonathan Doman } 587949f634dSJonathan Doman } 58816a2ced3SJonathan Doman 58916a2ced3SJonathan Doman SSTProviderRegistration(createMailbox); 59016a2ced3SJonathan Doman 59116a2ced3SJonathan Doman } // namespace sst 59216a2ced3SJonathan Doman } // namespace cpu_info 593