xref: /openbmc/smbios-mdr/src/sst_mailbox.cpp (revision c39d3dfca789d7ad5de9b7bd0856fc5b79c589df)
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  *
3016a2ced3SJonathan Doman  * Since multiple applications may be modifing 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;
4116a2ced3SJonathan Doman 
4216a2ced3SJonathan Doman     PECIManager(uint8_t address, CPUModel model) :
4316a2ced3SJonathan Doman         peciAddress(address), peciWoken(false), cpuModel(model)
4416a2ced3SJonathan Doman     {
4516a2ced3SJonathan Doman         mbBus = (model == icx) ? mbBusICX : mbBusOther;
4616a2ced3SJonathan Doman     }
4716a2ced3SJonathan Doman 
4816a2ced3SJonathan Doman     ~PECIManager()
4916a2ced3SJonathan Doman     {
5016a2ced3SJonathan Doman         // If we're being destroyed due to a PECIError, try to clear the mode
5116a2ced3SJonathan Doman         // bit, but catch and ignore any duplicate error it might raise to
5216a2ced3SJonathan Doman         // prevent termination.
5316a2ced3SJonathan Doman         try
5416a2ced3SJonathan Doman         {
5516a2ced3SJonathan Doman             if (peciWoken)
5616a2ced3SJonathan Doman             {
5716a2ced3SJonathan Doman                 setWakeOnPECI(false);
5816a2ced3SJonathan Doman             }
5916a2ced3SJonathan Doman         }
6016a2ced3SJonathan Doman         catch (const PECIError& err)
6116a2ced3SJonathan Doman         {}
6216a2ced3SJonathan Doman     }
6316a2ced3SJonathan Doman 
6416a2ced3SJonathan Doman     static bool isSleeping(EPECIStatus libStatus, uint8_t completionCode)
6516a2ced3SJonathan Doman     {
6616a2ced3SJonathan Doman         // PECI completion code defined in peci-ioctl.h which is not available
6716a2ced3SJonathan Doman         // for us to include.
6816a2ced3SJonathan Doman         constexpr int PECI_DEV_CC_UNAVAIL_RESOURCE = 0x82;
6916a2ced3SJonathan Doman         // Observed library returning DRIVER_ERR for reads and TIMEOUT for
7016a2ced3SJonathan Doman         // writes while PECI is sleeping. Either way, the completion code from
7116a2ced3SJonathan Doman         // PECI client should be reliable indicator of need to set WOP.
7216a2ced3SJonathan Doman         return libStatus != PECI_CC_SUCCESS &&
7316a2ced3SJonathan Doman                completionCode == PECI_DEV_CC_UNAVAIL_RESOURCE;
7416a2ced3SJonathan Doman     }
7516a2ced3SJonathan Doman 
7616a2ced3SJonathan Doman     /**
7716a2ced3SJonathan Doman      * Send a single PECI PCS write to modify the Wake-On-PECI mode bit
7816a2ced3SJonathan Doman      */
7916a2ced3SJonathan Doman     void setWakeOnPECI(bool enable)
8016a2ced3SJonathan Doman     {
8116a2ced3SJonathan Doman         uint8_t completionCode;
82*c39d3dfcSPatrick Williams         EPECIStatus libStatus = peci_WrPkgConfig(peciAddress, 5, enable ? 1 : 0,
83*c39d3dfcSPatrick Williams                                                  0, sizeof(uint32_t),
84*c39d3dfcSPatrick Williams                                                  &completionCode);
8516a2ced3SJonathan Doman         if (!checkPECIStatus(libStatus, completionCode))
8616a2ced3SJonathan Doman         {
8716a2ced3SJonathan Doman             throw PECIError("Failed to set Wake-On-PECI mode bit");
8816a2ced3SJonathan Doman         }
8916a2ced3SJonathan Doman 
9016a2ced3SJonathan Doman         if (enable)
9116a2ced3SJonathan Doman         {
9216a2ced3SJonathan Doman             peciWoken = true;
9316a2ced3SJonathan Doman         }
9416a2ced3SJonathan Doman     }
9516a2ced3SJonathan Doman 
9616a2ced3SJonathan Doman     // PCode OS Mailbox interface register locations
9716a2ced3SJonathan Doman     static constexpr int mbBusICX = 14;
9816a2ced3SJonathan Doman     static constexpr int mbBusOther = 31;
9916a2ced3SJonathan Doman     static constexpr int mbSegment = 0;
10016a2ced3SJonathan Doman     static constexpr int mbDevice = 30;
10116a2ced3SJonathan Doman     static constexpr int mbFunction = 1;
10216a2ced3SJonathan Doman     static constexpr int mbDataReg = 0xA0;
10316a2ced3SJonathan Doman     static constexpr int mbInterfaceReg = 0xA4;
10416a2ced3SJonathan Doman     static constexpr int mbRegSize = sizeof(uint32_t);
10516a2ced3SJonathan Doman 
10616a2ced3SJonathan Doman     enum class MailboxStatus
10716a2ced3SJonathan Doman     {
10816a2ced3SJonathan Doman         NoError = 0x0,
10916a2ced3SJonathan Doman         InvalidCommand = 0x1,
11016a2ced3SJonathan Doman         IllegalData = 0x16
11116a2ced3SJonathan Doman     };
11216a2ced3SJonathan Doman 
11316a2ced3SJonathan Doman     /**
11416a2ced3SJonathan Doman      * Send a single Write PCI Config Local command, targeting the PCU CR1
11516a2ced3SJonathan Doman      * register block.
11616a2ced3SJonathan Doman      *
11716a2ced3SJonathan Doman      * @param[in]   regAddress  PCI Offset of register.
11816a2ced3SJonathan Doman      * @param[in]   data        Data to write.
11916a2ced3SJonathan Doman      */
12016a2ced3SJonathan Doman     void wrMailboxReg(uint16_t regAddress, uint32_t data)
12116a2ced3SJonathan Doman     {
12216a2ced3SJonathan Doman         uint8_t completionCode;
12316a2ced3SJonathan Doman         bool tryWaking = true;
12416a2ced3SJonathan Doman         while (true)
12516a2ced3SJonathan Doman         {
12616a2ced3SJonathan Doman             EPECIStatus libStatus = peci_WrEndPointPCIConfigLocal(
12716a2ced3SJonathan Doman                 peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress,
12816a2ced3SJonathan Doman                 mbRegSize, data, &completionCode);
12916a2ced3SJonathan Doman             if (tryWaking && isSleeping(libStatus, completionCode))
13016a2ced3SJonathan Doman             {
13116a2ced3SJonathan Doman                 setWakeOnPECI(true);
13216a2ced3SJonathan Doman                 tryWaking = false;
13316a2ced3SJonathan Doman                 continue;
13416a2ced3SJonathan Doman             }
13516a2ced3SJonathan Doman             else if (!checkPECIStatus(libStatus, completionCode))
13616a2ced3SJonathan Doman             {
13716a2ced3SJonathan Doman                 throw PECIError("Failed to write mailbox reg");
13816a2ced3SJonathan Doman             }
13916a2ced3SJonathan Doman             break;
14016a2ced3SJonathan Doman         }
14116a2ced3SJonathan Doman     }
14216a2ced3SJonathan Doman 
14316a2ced3SJonathan Doman     /**
14416a2ced3SJonathan Doman      * Send a single Read PCI Config Local command, targeting the PCU CR1
14516a2ced3SJonathan Doman      * register block.
14616a2ced3SJonathan Doman      *
14716a2ced3SJonathan Doman      * @param[in]   regAddress  PCI offset of register.
14816a2ced3SJonathan Doman      *
14916a2ced3SJonathan Doman      * @return  Register value
15016a2ced3SJonathan Doman      */
15116a2ced3SJonathan Doman     uint32_t rdMailboxReg(uint16_t regAddress)
15216a2ced3SJonathan Doman     {
15316a2ced3SJonathan Doman         uint8_t completionCode;
15416a2ced3SJonathan Doman         uint32_t outputData;
15516a2ced3SJonathan Doman         bool tryWaking = true;
15616a2ced3SJonathan Doman         while (true)
15716a2ced3SJonathan Doman         {
15816a2ced3SJonathan Doman             EPECIStatus libStatus = peci_RdEndPointConfigPciLocal(
15916a2ced3SJonathan Doman                 peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress,
16016a2ced3SJonathan Doman                 mbRegSize, reinterpret_cast<uint8_t*>(&outputData),
16116a2ced3SJonathan Doman                 &completionCode);
16216a2ced3SJonathan Doman             if (tryWaking && isSleeping(libStatus, completionCode))
16316a2ced3SJonathan Doman             {
16416a2ced3SJonathan Doman                 setWakeOnPECI(true);
16516a2ced3SJonathan Doman                 tryWaking = false;
16616a2ced3SJonathan Doman                 continue;
16716a2ced3SJonathan Doman             }
16816a2ced3SJonathan Doman             if (!checkPECIStatus(libStatus, completionCode))
16916a2ced3SJonathan Doman             {
17016a2ced3SJonathan Doman                 throw PECIError("Failed to read mailbox reg");
17116a2ced3SJonathan Doman             }
17216a2ced3SJonathan Doman             break;
17316a2ced3SJonathan Doman         }
17416a2ced3SJonathan Doman         return outputData;
17516a2ced3SJonathan Doman     }
17616a2ced3SJonathan Doman 
17716a2ced3SJonathan Doman     /**
17816a2ced3SJonathan Doman      * Send command on PCode OS Mailbox interface.
17916a2ced3SJonathan Doman      *
18016a2ced3SJonathan Doman      * @param[in]   command     Main command ID.
18116a2ced3SJonathan Doman      * @param[in]   subCommand  Sub command ID.
18216a2ced3SJonathan Doman      * @param[in]   inputData   Data to put in mailbox. Is always written, but
18316a2ced3SJonathan Doman      *                          will be ignored by PCode if command is a
18416a2ced3SJonathan Doman      *                          "getter".
18516a2ced3SJonathan Doman      * @param[out]  responseCode    Optional parameter to receive the
18616a2ced3SJonathan Doman      *                              mailbox-level response status. If null, a
18716a2ced3SJonathan Doman      *                              PECIError will be thrown for error status.
18816a2ced3SJonathan Doman      *
18916a2ced3SJonathan Doman      * @return  Data returned in mailbox. Value is undefined if command is a
19016a2ced3SJonathan Doman      *          "setter".
19116a2ced3SJonathan Doman      */
19216a2ced3SJonathan Doman     uint32_t sendPECIOSMailboxCmd(uint8_t command, uint8_t subCommand,
19316a2ced3SJonathan Doman                                   uint32_t inputData = 0,
19416a2ced3SJonathan Doman                                   MailboxStatus* responseCode = nullptr)
19516a2ced3SJonathan Doman     {
19616a2ced3SJonathan Doman         // The simple mailbox algorithm just says to wait until the busy bit
19716a2ced3SJonathan Doman         // is clear, but we'll give up after 10 tries. It's arbitrary but that's
19816a2ced3SJonathan Doman         // quite long wall clock time.
19916a2ced3SJonathan Doman         constexpr int mbRetries = 10;
20016a2ced3SJonathan Doman         constexpr uint32_t mbBusyBit = bit(31);
20116a2ced3SJonathan Doman 
20216a2ced3SJonathan Doman         // Wait until RUN_BUSY == 0
20316a2ced3SJonathan Doman         int attempts = mbRetries;
20416a2ced3SJonathan Doman         while ((rdMailboxReg(mbInterfaceReg) & mbBusyBit) != 0 &&
20516a2ced3SJonathan Doman                --attempts > 0)
20616a2ced3SJonathan Doman             ;
20716a2ced3SJonathan Doman         if (attempts == 0)
20816a2ced3SJonathan Doman         {
20916a2ced3SJonathan Doman             throw PECIError("OS Mailbox failed to become free");
21016a2ced3SJonathan Doman         }
21116a2ced3SJonathan Doman 
21216a2ced3SJonathan Doman         // Write required command specific input data to data register
21316a2ced3SJonathan Doman         wrMailboxReg(mbDataReg, inputData);
21416a2ced3SJonathan Doman 
21516a2ced3SJonathan Doman         // Write required command specific command/sub-command values and set
21616a2ced3SJonathan Doman         // RUN_BUSY bit in interface register.
21716a2ced3SJonathan Doman         uint32_t interfaceReg =
21816a2ced3SJonathan Doman             mbBusyBit | (static_cast<uint32_t>(subCommand) << 8) | command;
21916a2ced3SJonathan Doman         wrMailboxReg(mbInterfaceReg, interfaceReg);
22016a2ced3SJonathan Doman 
22116a2ced3SJonathan Doman         // Wait until RUN_BUSY == 0
22216a2ced3SJonathan Doman         attempts = mbRetries;
22316a2ced3SJonathan Doman         do
22416a2ced3SJonathan Doman         {
22516a2ced3SJonathan Doman             interfaceReg = rdMailboxReg(mbInterfaceReg);
22616a2ced3SJonathan Doman         } while ((interfaceReg & mbBusyBit) != 0 && --attempts > 0);
22716a2ced3SJonathan Doman         if (attempts == 0)
22816a2ced3SJonathan Doman         {
22916a2ced3SJonathan Doman             throw PECIError("OS Mailbox failed to return");
23016a2ced3SJonathan Doman         }
23116a2ced3SJonathan Doman 
23216a2ced3SJonathan Doman         // Read command return status or error code from interface register
23316a2ced3SJonathan Doman         auto status = static_cast<MailboxStatus>(interfaceReg & 0xFF);
23416a2ced3SJonathan Doman         if (responseCode != nullptr)
23516a2ced3SJonathan Doman         {
23616a2ced3SJonathan Doman             *responseCode = status;
23716a2ced3SJonathan Doman         }
23816a2ced3SJonathan Doman         else if (status != MailboxStatus::NoError)
23916a2ced3SJonathan Doman         {
24016a2ced3SJonathan Doman             throw PECIError(std::string("OS Mailbox returned with error: ") +
24116a2ced3SJonathan Doman                             std::to_string(static_cast<int>(status)));
24216a2ced3SJonathan Doman         }
24316a2ced3SJonathan Doman 
24416a2ced3SJonathan Doman         // Read command return data from the data register
24516a2ced3SJonathan Doman         return rdMailboxReg(mbDataReg);
24616a2ced3SJonathan Doman     }
24716a2ced3SJonathan Doman };
24816a2ced3SJonathan Doman 
24916a2ced3SJonathan Doman /**
25016a2ced3SJonathan Doman  * Base class for set of PECI OS Mailbox commands.
25116a2ced3SJonathan Doman  * Constructing it runs the command and stores the value for use by derived
25216a2ced3SJonathan Doman  * class accessor methods.
25316a2ced3SJonathan Doman  */
25416a2ced3SJonathan Doman template <uint8_t subcommand>
25516a2ced3SJonathan Doman struct OsMailboxCommand
25616a2ced3SJonathan Doman {
25716a2ced3SJonathan Doman     enum ErrorPolicy
25816a2ced3SJonathan Doman     {
25916a2ced3SJonathan Doman         Throw,
26016a2ced3SJonathan Doman         NoThrow
26116a2ced3SJonathan Doman     };
26216a2ced3SJonathan Doman 
26316a2ced3SJonathan Doman     uint32_t value;
26416a2ced3SJonathan Doman     PECIManager::MailboxStatus status;
26516a2ced3SJonathan Doman     /**
26616a2ced3SJonathan Doman      * Construct the command object with required PECI address and up to 4
26716a2ced3SJonathan Doman      * optional 1-byte input data parameters.
26816a2ced3SJonathan Doman      */
26916a2ced3SJonathan Doman     OsMailboxCommand(PECIManager& pm, uint8_t param1 = 0, uint8_t param2 = 0,
27016a2ced3SJonathan Doman                      uint8_t param3 = 0, uint8_t param4 = 0) :
27116a2ced3SJonathan Doman         OsMailboxCommand(pm, ErrorPolicy::Throw, param1, param2, param3, param4)
27216a2ced3SJonathan Doman     {}
27316a2ced3SJonathan Doman 
27416a2ced3SJonathan Doman     OsMailboxCommand(PECIManager& pm, ErrorPolicy errorPolicy,
27516a2ced3SJonathan Doman                      uint8_t param1 = 0, uint8_t param2 = 0, uint8_t param3 = 0,
27616a2ced3SJonathan Doman                      uint8_t param4 = 0)
27716a2ced3SJonathan Doman     {
27816a2ced3SJonathan Doman         DEBUG_PRINT << "Running OS Mailbox command "
27916a2ced3SJonathan Doman                     << static_cast<int>(subcommand) << '\n';
280*c39d3dfcSPatrick Williams         PECIManager::MailboxStatus* callStatus = errorPolicy == Throw ? nullptr
281*c39d3dfcSPatrick Williams                                                                       : &status;
28216a2ced3SJonathan Doman         uint32_t param = (static_cast<uint32_t>(param4) << 24) |
28316a2ced3SJonathan Doman                          (static_cast<uint32_t>(param3) << 16) |
28416a2ced3SJonathan Doman                          (static_cast<uint32_t>(param2) << 8) | param1;
28516a2ced3SJonathan Doman         value = pm.sendPECIOSMailboxCmd(0x7F, subcommand, param, callStatus);
28616a2ced3SJonathan Doman     }
28716a2ced3SJonathan Doman 
28816a2ced3SJonathan Doman     /** Return whether the mailbox status indicated success or not. */
28916a2ced3SJonathan Doman     bool success() const
29016a2ced3SJonathan Doman     {
29116a2ced3SJonathan Doman         return status == PECIManager::MailboxStatus::NoError;
29216a2ced3SJonathan Doman     }
29316a2ced3SJonathan Doman };
29416a2ced3SJonathan Doman 
29516a2ced3SJonathan Doman /**
29616a2ced3SJonathan Doman  * Macro to define a derived class accessor method.
29716a2ced3SJonathan Doman  *
29816a2ced3SJonathan Doman  * @param[in]   type    Return type of accessor method.
29916a2ced3SJonathan Doman  * @param[in]   name    Name of accessor method.
30016a2ced3SJonathan Doman  * @param[in]   hibit   Most significant bit of field to access.
30116a2ced3SJonathan Doman  * @param[in]   lobit   Least significant bit of field to access.
30216a2ced3SJonathan Doman  */
30316a2ced3SJonathan Doman #define FIELD(type, name, hibit, lobit)                                        \
30416a2ced3SJonathan Doman     type name() const                                                          \
30516a2ced3SJonathan Doman     {                                                                          \
30616a2ced3SJonathan Doman         return (value >> lobit) & (bit(hibit - lobit + 1) - 1);                \
30716a2ced3SJonathan Doman     }
30816a2ced3SJonathan Doman 
30916a2ced3SJonathan Doman struct GetLevelsInfo : OsMailboxCommand<0x0>
31016a2ced3SJonathan Doman {
31116a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
31216a2ced3SJonathan Doman     FIELD(bool, enabled, 31, 31)
31316a2ced3SJonathan Doman     FIELD(bool, lock, 24, 24)
31416a2ced3SJonathan Doman     FIELD(unsigned, currentConfigTdpLevel, 23, 16)
31516a2ced3SJonathan Doman     FIELD(unsigned, configTdpLevels, 15, 8)
31616a2ced3SJonathan Doman     FIELD(unsigned, version, 7, 0)
31716a2ced3SJonathan Doman };
31816a2ced3SJonathan Doman 
31916a2ced3SJonathan Doman struct GetConfigTdpControl : OsMailboxCommand<0x1>
32016a2ced3SJonathan Doman {
32116a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
32216a2ced3SJonathan Doman     FIELD(bool, pbfEnabled, 17, 17);
32316a2ced3SJonathan Doman     FIELD(bool, factEnabled, 16, 16);
32416a2ced3SJonathan Doman     FIELD(bool, pbfSupport, 1, 1);
32516a2ced3SJonathan Doman     FIELD(bool, factSupport, 0, 0);
32616a2ced3SJonathan Doman };
32716a2ced3SJonathan Doman 
32816a2ced3SJonathan Doman struct SetConfigTdpControl : OsMailboxCommand<0x2>
32916a2ced3SJonathan Doman {
33016a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
33116a2ced3SJonathan Doman };
33216a2ced3SJonathan Doman 
33316a2ced3SJonathan Doman struct GetTdpInfo : OsMailboxCommand<0x3>
33416a2ced3SJonathan Doman {
33516a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
33616a2ced3SJonathan Doman     FIELD(unsigned, tdpRatio, 23, 16);
33716a2ced3SJonathan Doman     FIELD(unsigned, pkgTdp, 14, 0);
33816a2ced3SJonathan Doman };
33916a2ced3SJonathan Doman 
34016a2ced3SJonathan Doman struct GetCoreMask : OsMailboxCommand<0x6>
34116a2ced3SJonathan Doman {
34216a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
34316a2ced3SJonathan Doman     FIELD(uint32_t, coresMask, 31, 0);
34416a2ced3SJonathan Doman };
34516a2ced3SJonathan Doman 
34616a2ced3SJonathan Doman struct GetTurboLimitRatios : OsMailboxCommand<0x7>
34716a2ced3SJonathan Doman {
34816a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
34916a2ced3SJonathan Doman };
35016a2ced3SJonathan Doman 
35116a2ced3SJonathan Doman struct SetLevel : OsMailboxCommand<0x8>
35216a2ced3SJonathan Doman {
35316a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
35416a2ced3SJonathan Doman };
35516a2ced3SJonathan Doman 
35616a2ced3SJonathan Doman struct GetRatioInfo : OsMailboxCommand<0xC>
35716a2ced3SJonathan Doman {
35816a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
35916a2ced3SJonathan Doman     FIELD(unsigned, pm, 31, 24);
36016a2ced3SJonathan Doman     FIELD(unsigned, pn, 23, 16);
36116a2ced3SJonathan Doman     FIELD(unsigned, p1, 15, 8);
36216a2ced3SJonathan Doman     FIELD(unsigned, p0, 7, 0);
36316a2ced3SJonathan Doman };
36416a2ced3SJonathan Doman 
36516a2ced3SJonathan Doman struct GetTjmaxInfo : OsMailboxCommand<0x5>
36616a2ced3SJonathan Doman {
36716a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
36816a2ced3SJonathan Doman     FIELD(unsigned, tProchot, 7, 0);
36916a2ced3SJonathan Doman };
37016a2ced3SJonathan Doman 
37116a2ced3SJonathan Doman struct PbfGetCoreMaskInfo : OsMailboxCommand<0x20>
37216a2ced3SJonathan Doman {
37316a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
37416a2ced3SJonathan Doman     FIELD(uint32_t, p1HiCoreMask, 31, 0);
37516a2ced3SJonathan Doman };
37616a2ced3SJonathan Doman 
37716a2ced3SJonathan Doman struct PbfGetP1HiP1LoInfo : OsMailboxCommand<0x21>
37816a2ced3SJonathan Doman {
37916a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
38016a2ced3SJonathan Doman     FIELD(unsigned, p1Hi, 15, 8);
38116a2ced3SJonathan Doman     FIELD(unsigned, p1Lo, 7, 0);
38216a2ced3SJonathan Doman };
38316a2ced3SJonathan Doman 
38416a2ced3SJonathan Doman /**
38516a2ced3SJonathan Doman  * Implementation of SSTInterface based on OS Mailbox interface supported on ICX
38616a2ced3SJonathan Doman  * and SPR processors.
38716a2ced3SJonathan Doman  * It's expected that an instance of this class will be created for each
38816a2ced3SJonathan Doman  * "atomic" set of operations.
38916a2ced3SJonathan Doman  */
39016a2ced3SJonathan Doman class SSTMailbox : public SSTInterface
39116a2ced3SJonathan Doman {
39216a2ced3SJonathan Doman   private:
39316a2ced3SJonathan Doman     uint8_t address;
39416a2ced3SJonathan Doman     CPUModel model;
39516a2ced3SJonathan Doman     PECIManager pm;
39616a2ced3SJonathan Doman 
39716a2ced3SJonathan Doman     static constexpr int mhzPerRatio = 100;
39816a2ced3SJonathan Doman 
39916a2ced3SJonathan Doman   public:
40016a2ced3SJonathan Doman     SSTMailbox(uint8_t _address, CPUModel _model) :
40116a2ced3SJonathan Doman         address(_address), model(_model),
40216a2ced3SJonathan Doman         pm(static_cast<uint8_t>(address), model)
40316a2ced3SJonathan Doman     {}
404*c39d3dfcSPatrick Williams     ~SSTMailbox() {}
40516a2ced3SJonathan Doman 
40616a2ced3SJonathan Doman     bool ready() override
40716a2ced3SJonathan Doman     {
40816a2ced3SJonathan Doman         return true;
40916a2ced3SJonathan Doman     }
41016a2ced3SJonathan Doman 
41116a2ced3SJonathan Doman     bool supportsControl() override
41216a2ced3SJonathan Doman     {
413949f634dSJonathan Doman         switch (model)
414949f634dSJonathan Doman         {
415949f634dSJonathan Doman             case spr:
416949f634dSJonathan Doman             case emr:
417949f634dSJonathan Doman                 return true;
418949f634dSJonathan Doman             default:
419949f634dSJonathan Doman                 return false;
420949f634dSJonathan Doman         }
42116a2ced3SJonathan Doman     }
42216a2ced3SJonathan Doman 
42316a2ced3SJonathan Doman     unsigned int currentLevel() override
42416a2ced3SJonathan Doman     {
42516a2ced3SJonathan Doman         return GetLevelsInfo(pm).currentConfigTdpLevel();
42616a2ced3SJonathan Doman     }
427b4c3bcd7SJonathan Doman     unsigned int maxLevel() override
42816a2ced3SJonathan Doman     {
42916a2ced3SJonathan Doman         return GetLevelsInfo(pm).configTdpLevels();
43016a2ced3SJonathan Doman     }
43116a2ced3SJonathan Doman     bool ppEnabled() override
43216a2ced3SJonathan Doman     {
43316a2ced3SJonathan Doman         return GetLevelsInfo(pm).enabled();
43416a2ced3SJonathan Doman     }
43516a2ced3SJonathan Doman 
43616a2ced3SJonathan Doman     bool levelSupported(unsigned int level) override
43716a2ced3SJonathan Doman     {
43816a2ced3SJonathan Doman         GetConfigTdpControl tdpControl(
43916a2ced3SJonathan Doman             pm, GetConfigTdpControl::ErrorPolicy::NoThrow,
44016a2ced3SJonathan Doman             static_cast<uint8_t>(level));
44116a2ced3SJonathan Doman         return tdpControl.success();
44216a2ced3SJonathan Doman     }
44316a2ced3SJonathan Doman     bool bfSupported(unsigned int level) override
44416a2ced3SJonathan Doman     {
44516a2ced3SJonathan Doman         return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
44616a2ced3SJonathan Doman             .pbfSupport();
44716a2ced3SJonathan Doman     }
44816a2ced3SJonathan Doman     bool tfSupported(unsigned int level) override
44916a2ced3SJonathan Doman     {
45016a2ced3SJonathan Doman         return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
45116a2ced3SJonathan Doman             .factSupport();
45216a2ced3SJonathan Doman     }
45316a2ced3SJonathan Doman     bool bfEnabled(unsigned int level) override
45416a2ced3SJonathan Doman     {
45516a2ced3SJonathan Doman         return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
45616a2ced3SJonathan Doman             .pbfEnabled();
45716a2ced3SJonathan Doman     }
45816a2ced3SJonathan Doman     bool tfEnabled(unsigned int level) override
45916a2ced3SJonathan Doman     {
46016a2ced3SJonathan Doman         return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
46116a2ced3SJonathan Doman             .factEnabled();
46216a2ced3SJonathan Doman     }
46316a2ced3SJonathan Doman     unsigned int tdp(unsigned int level) override
46416a2ced3SJonathan Doman     {
46516a2ced3SJonathan Doman         return GetTdpInfo(pm, static_cast<uint8_t>(level)).pkgTdp();
46616a2ced3SJonathan Doman     }
46716a2ced3SJonathan Doman     unsigned int coreCount(unsigned int level) override
46816a2ced3SJonathan Doman     {
46916a2ced3SJonathan Doman         return enabledCoreList(level).size();
47016a2ced3SJonathan Doman     }
47116a2ced3SJonathan Doman     std::vector<unsigned int> enabledCoreList(unsigned int level) override
47216a2ced3SJonathan Doman     {
47316a2ced3SJonathan Doman         uint64_t coreMaskLo =
47416a2ced3SJonathan Doman             GetCoreMask(pm, static_cast<uint8_t>(level), 0).coresMask();
47516a2ced3SJonathan Doman         uint64_t coreMaskHi =
47616a2ced3SJonathan Doman             GetCoreMask(pm, static_cast<uint8_t>(level), 1).coresMask();
47716a2ced3SJonathan Doman         std::bitset<64> coreMask = (coreMaskHi << 32 | coreMaskLo);
47816a2ced3SJonathan Doman         return convertMaskToList(coreMask);
47916a2ced3SJonathan Doman     }
48016a2ced3SJonathan Doman     std::vector<TurboEntry> sseTurboProfile(unsigned int level) override
48116a2ced3SJonathan Doman     {
48216a2ced3SJonathan Doman         // Read the Turbo Ratio Limit Cores MSR which is used to generate the
48316a2ced3SJonathan Doman         // Turbo Profile for each profile. This is a package scope MSR, so just
48416a2ced3SJonathan Doman         // read thread 0.
48516a2ced3SJonathan Doman         uint64_t trlCores;
48616a2ced3SJonathan Doman         uint8_t cc;
48716a2ced3SJonathan Doman         EPECIStatus status = peci_RdIAMSR(static_cast<uint8_t>(address), 0,
48816a2ced3SJonathan Doman                                           0x1AE, &trlCores, &cc);
48916a2ced3SJonathan Doman         if (!checkPECIStatus(status, cc))
49016a2ced3SJonathan Doman         {
49116a2ced3SJonathan Doman             throw PECIError("Failed to read TRL MSR");
49216a2ced3SJonathan Doman         }
49316a2ced3SJonathan Doman 
49416a2ced3SJonathan Doman         std::vector<TurboEntry> turboSpeeds;
49516a2ced3SJonathan Doman         uint64_t limitRatioLo =
49616a2ced3SJonathan Doman             GetTurboLimitRatios(pm, static_cast<uint8_t>(level), 0, 0).value;
49716a2ced3SJonathan Doman         uint64_t limitRatioHi =
49816a2ced3SJonathan Doman             GetTurboLimitRatios(pm, static_cast<uint8_t>(level), 1, 0).value;
49916a2ced3SJonathan Doman         uint64_t limitRatios = (limitRatioHi << 32) | limitRatioLo;
50016a2ced3SJonathan Doman 
50116a2ced3SJonathan Doman         constexpr int maxTFBuckets = 8;
50216a2ced3SJonathan Doman         for (int i = 0; i < maxTFBuckets; ++i)
50316a2ced3SJonathan Doman         {
50416a2ced3SJonathan Doman             size_t bucketCount = trlCores & 0xFF;
50516a2ced3SJonathan Doman             int bucketSpeed = limitRatios & 0xFF;
50616a2ced3SJonathan Doman             if (bucketCount != 0 && bucketSpeed != 0)
50716a2ced3SJonathan Doman             {
50816a2ced3SJonathan Doman                 turboSpeeds.push_back({bucketSpeed * mhzPerRatio, bucketCount});
50916a2ced3SJonathan Doman             }
51016a2ced3SJonathan Doman 
51116a2ced3SJonathan Doman             trlCores >>= 8;
51216a2ced3SJonathan Doman             limitRatios >>= 8;
51316a2ced3SJonathan Doman         }
51416a2ced3SJonathan Doman         return turboSpeeds;
51516a2ced3SJonathan Doman     }
51616a2ced3SJonathan Doman     unsigned int p1Freq(unsigned int level) override
51716a2ced3SJonathan Doman     {
51816a2ced3SJonathan Doman         return GetRatioInfo(pm, static_cast<uint8_t>(level)).p1() * mhzPerRatio;
51916a2ced3SJonathan Doman     }
52016a2ced3SJonathan Doman     unsigned int p0Freq(unsigned int level) override
52116a2ced3SJonathan Doman     {
52216a2ced3SJonathan Doman         return GetRatioInfo(pm, static_cast<uint8_t>(level)).p0() * mhzPerRatio;
52316a2ced3SJonathan Doman     }
52416a2ced3SJonathan Doman     unsigned int prochotTemp(unsigned int level) override
52516a2ced3SJonathan Doman     {
52616a2ced3SJonathan Doman         return GetTjmaxInfo(pm, static_cast<uint8_t>(level)).tProchot();
52716a2ced3SJonathan Doman     }
52816a2ced3SJonathan Doman     std::vector<unsigned int>
52916a2ced3SJonathan Doman         bfHighPriorityCoreList(unsigned int level) override
53016a2ced3SJonathan Doman     {
531*c39d3dfcSPatrick Williams         uint64_t coreMaskLo = PbfGetCoreMaskInfo(pm,
532*c39d3dfcSPatrick Williams                                                  static_cast<uint8_t>(level), 0)
53316a2ced3SJonathan Doman                                   .p1HiCoreMask();
534*c39d3dfcSPatrick Williams         uint64_t coreMaskHi = PbfGetCoreMaskInfo(pm,
535*c39d3dfcSPatrick Williams                                                  static_cast<uint8_t>(level), 1)
53616a2ced3SJonathan Doman                                   .p1HiCoreMask();
53716a2ced3SJonathan Doman         std::bitset<64> hiFreqCoreList = (coreMaskHi << 32) | coreMaskLo;
53816a2ced3SJonathan Doman         return convertMaskToList(hiFreqCoreList);
53916a2ced3SJonathan Doman     }
54016a2ced3SJonathan Doman     unsigned int bfHighPriorityFreq(unsigned int level) override
54116a2ced3SJonathan Doman     {
54216a2ced3SJonathan Doman         return PbfGetP1HiP1LoInfo(pm, static_cast<uint8_t>(level)).p1Hi() *
54316a2ced3SJonathan Doman                mhzPerRatio;
54416a2ced3SJonathan Doman     }
54516a2ced3SJonathan Doman     unsigned int bfLowPriorityFreq(unsigned int level) override
54616a2ced3SJonathan Doman     {
54716a2ced3SJonathan Doman         return PbfGetP1HiP1LoInfo(pm, static_cast<uint8_t>(level)).p1Lo() *
54816a2ced3SJonathan Doman                mhzPerRatio;
54916a2ced3SJonathan Doman     }
55016a2ced3SJonathan Doman 
55116a2ced3SJonathan Doman     void setBfEnabled(bool enable) override
55216a2ced3SJonathan Doman     {
55316a2ced3SJonathan Doman         GetConfigTdpControl getTDPControl(pm);
55416a2ced3SJonathan Doman         bool tfEnabled = false;
55516a2ced3SJonathan Doman         uint8_t param = (enable ? bit(1) : 0) | (tfEnabled ? bit(0) : 0);
55616a2ced3SJonathan Doman         SetConfigTdpControl(pm, 0, 0, param);
55716a2ced3SJonathan Doman     }
55816a2ced3SJonathan Doman     void setTfEnabled(bool enable) override
55916a2ced3SJonathan Doman     {
56016a2ced3SJonathan Doman         // TODO: use cached BF value
56116a2ced3SJonathan Doman         bool bfEnabled = false;
56216a2ced3SJonathan Doman         uint8_t param = (bfEnabled ? bit(1) : 0) | (enable ? bit(0) : 0);
56316a2ced3SJonathan Doman         SetConfigTdpControl(pm, 0, 0, param);
56416a2ced3SJonathan Doman     }
56516a2ced3SJonathan Doman     void setCurrentLevel(unsigned int level) override
56616a2ced3SJonathan Doman     {
56716a2ced3SJonathan Doman         SetLevel(pm, static_cast<uint8_t>(level));
56816a2ced3SJonathan Doman     }
56916a2ced3SJonathan Doman };
57016a2ced3SJonathan Doman 
57116a2ced3SJonathan Doman static std::unique_ptr<SSTInterface> createMailbox(uint8_t address,
57216a2ced3SJonathan Doman                                                    CPUModel model)
57316a2ced3SJonathan Doman {
57416a2ced3SJonathan Doman     DEBUG_PRINT << "createMailbox\n";
575949f634dSJonathan Doman     switch (model)
57616a2ced3SJonathan Doman     {
577949f634dSJonathan Doman         case icx:
578949f634dSJonathan Doman         case icxd:
579949f634dSJonathan Doman         case spr:
580949f634dSJonathan Doman         case emr:
58116a2ced3SJonathan Doman             return std::make_unique<SSTMailbox>(address, model);
582949f634dSJonathan Doman         default:
58316a2ced3SJonathan Doman             return nullptr;
58416a2ced3SJonathan Doman     }
585949f634dSJonathan Doman }
58616a2ced3SJonathan Doman 
58716a2ced3SJonathan Doman SSTProviderRegistration(createMailbox);
58816a2ced3SJonathan Doman 
58916a2ced3SJonathan Doman } // namespace sst
59016a2ced3SJonathan Doman } // namespace cpu_info
591