xref: /openbmc/smbios-mdr/src/sst_mailbox.cpp (revision 1d73dccc89f0bb9d1dce3543e5af6b3e3087d5f4)
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