| Jonathan Doman | 16a2ced | 2021-11-01 11:13:22 -0700 | [diff] [blame] | 1 | // Copyright (c) 2022 Intel Corporation | 
 | 2 | // | 
 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 4 | // you may not use this file except in compliance with the License. | 
 | 5 | // You may obtain a copy of the License at | 
 | 6 | // | 
 | 7 | //      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 8 | // | 
 | 9 | // Unless required by applicable law or agreed to in writing, software | 
 | 10 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 12 | // See the License for the specific language governing permissions and | 
 | 13 | // limitations under the License. | 
 | 14 |  | 
 | 15 | #include "cpuinfo_utils.hpp" | 
 | 16 | #include "speed_select.hpp" | 
 | 17 |  | 
 | 18 | #include <iostream> | 
 | 19 |  | 
 | 20 | namespace cpu_info | 
 | 21 | { | 
 | 22 | namespace sst | 
 | 23 | { | 
 | 24 |  | 
 | 25 | /** | 
 | 26 |  * Convenience RAII object for Wake-On-PECI (WOP) management, since PECI Config | 
 | 27 |  * Local accesses to the OS Mailbox require the package to pop up to PC2. Also | 
 | 28 |  * provides PCode OS Mailbox routine. | 
 | 29 |  * | 
 | 30 |  * Since multiple applications may be modifing WOP, we'll use this algorithm: | 
 | 31 |  * Whenever a PECI command fails with associated error code, set WOP bit and | 
 | 32 |  * retry command. Upon manager destruction, clear WOP bit only if we previously | 
 | 33 |  * set it. | 
 | 34 |  */ | 
 | 35 | struct PECIManager | 
 | 36 | { | 
 | 37 |     uint8_t peciAddress; | 
 | 38 |     bool peciWoken; | 
 | 39 |     CPUModel cpuModel; | 
 | 40 |     uint8_t mbBus; | 
 | 41 |  | 
 | 42 |     PECIManager(uint8_t address, CPUModel model) : | 
 | 43 |         peciAddress(address), peciWoken(false), cpuModel(model) | 
 | 44 |     { | 
 | 45 |         mbBus = (model == icx) ? mbBusICX : mbBusOther; | 
 | 46 |     } | 
 | 47 |  | 
 | 48 |     ~PECIManager() | 
 | 49 |     { | 
 | 50 |         // If we're being destroyed due to a PECIError, try to clear the mode | 
 | 51 |         // bit, but catch and ignore any duplicate error it might raise to | 
 | 52 |         // prevent termination. | 
 | 53 |         try | 
 | 54 |         { | 
 | 55 |             if (peciWoken) | 
 | 56 |             { | 
 | 57 |                 setWakeOnPECI(false); | 
 | 58 |             } | 
 | 59 |         } | 
 | 60 |         catch (const PECIError& err) | 
 | 61 |         {} | 
 | 62 |     } | 
 | 63 |  | 
 | 64 |     static bool isSleeping(EPECIStatus libStatus, uint8_t completionCode) | 
 | 65 |     { | 
 | 66 |         // PECI completion code defined in peci-ioctl.h which is not available | 
 | 67 |         // for us to include. | 
 | 68 |         constexpr int PECI_DEV_CC_UNAVAIL_RESOURCE = 0x82; | 
 | 69 |         // Observed library returning DRIVER_ERR for reads and TIMEOUT for | 
 | 70 |         // writes while PECI is sleeping. Either way, the completion code from | 
 | 71 |         // PECI client should be reliable indicator of need to set WOP. | 
 | 72 |         return libStatus != PECI_CC_SUCCESS && | 
 | 73 |                completionCode == PECI_DEV_CC_UNAVAIL_RESOURCE; | 
 | 74 |     } | 
 | 75 |  | 
 | 76 |     /** | 
 | 77 |      * Send a single PECI PCS write to modify the Wake-On-PECI mode bit | 
 | 78 |      */ | 
 | 79 |     void setWakeOnPECI(bool enable) | 
 | 80 |     { | 
 | 81 |         uint8_t completionCode; | 
 | 82 |         EPECIStatus libStatus = | 
 | 83 |             peci_WrPkgConfig(peciAddress, 5, enable ? 1 : 0, 0, | 
 | 84 |                              sizeof(uint32_t), &completionCode); | 
 | 85 |         if (!checkPECIStatus(libStatus, completionCode)) | 
 | 86 |         { | 
 | 87 |             throw PECIError("Failed to set Wake-On-PECI mode bit"); | 
 | 88 |         } | 
 | 89 |  | 
 | 90 |         if (enable) | 
 | 91 |         { | 
 | 92 |             peciWoken = true; | 
 | 93 |         } | 
 | 94 |     } | 
 | 95 |  | 
 | 96 |     // PCode OS Mailbox interface register locations | 
 | 97 |     static constexpr int mbBusICX = 14; | 
 | 98 |     static constexpr int mbBusOther = 31; | 
 | 99 |     static constexpr int mbSegment = 0; | 
 | 100 |     static constexpr int mbDevice = 30; | 
 | 101 |     static constexpr int mbFunction = 1; | 
 | 102 |     static constexpr int mbDataReg = 0xA0; | 
 | 103 |     static constexpr int mbInterfaceReg = 0xA4; | 
 | 104 |     static constexpr int mbRegSize = sizeof(uint32_t); | 
 | 105 |  | 
 | 106 |     enum class MailboxStatus | 
 | 107 |     { | 
 | 108 |         NoError = 0x0, | 
 | 109 |         InvalidCommand = 0x1, | 
 | 110 |         IllegalData = 0x16 | 
 | 111 |     }; | 
 | 112 |  | 
 | 113 |     /** | 
 | 114 |      * Send a single Write PCI Config Local command, targeting the PCU CR1 | 
 | 115 |      * register block. | 
 | 116 |      * | 
 | 117 |      * @param[in]   regAddress  PCI Offset of register. | 
 | 118 |      * @param[in]   data        Data to write. | 
 | 119 |      */ | 
 | 120 |     void wrMailboxReg(uint16_t regAddress, uint32_t data) | 
 | 121 |     { | 
 | 122 |         uint8_t completionCode; | 
 | 123 |         bool tryWaking = true; | 
 | 124 |         while (true) | 
 | 125 |         { | 
 | 126 |             EPECIStatus libStatus = peci_WrEndPointPCIConfigLocal( | 
 | 127 |                 peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress, | 
 | 128 |                 mbRegSize, data, &completionCode); | 
 | 129 |             if (tryWaking && isSleeping(libStatus, completionCode)) | 
 | 130 |             { | 
 | 131 |                 setWakeOnPECI(true); | 
 | 132 |                 tryWaking = false; | 
 | 133 |                 continue; | 
 | 134 |             } | 
 | 135 |             else if (!checkPECIStatus(libStatus, completionCode)) | 
 | 136 |             { | 
 | 137 |                 throw PECIError("Failed to write mailbox reg"); | 
 | 138 |             } | 
 | 139 |             break; | 
 | 140 |         } | 
 | 141 |     } | 
 | 142 |  | 
 | 143 |     /** | 
 | 144 |      * Send a single Read PCI Config Local command, targeting the PCU CR1 | 
 | 145 |      * register block. | 
 | 146 |      * | 
 | 147 |      * @param[in]   regAddress  PCI offset of register. | 
 | 148 |      * | 
 | 149 |      * @return  Register value | 
 | 150 |      */ | 
 | 151 |     uint32_t rdMailboxReg(uint16_t regAddress) | 
 | 152 |     { | 
 | 153 |         uint8_t completionCode; | 
 | 154 |         uint32_t outputData; | 
 | 155 |         bool tryWaking = true; | 
 | 156 |         while (true) | 
 | 157 |         { | 
 | 158 |             EPECIStatus libStatus = peci_RdEndPointConfigPciLocal( | 
 | 159 |                 peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress, | 
 | 160 |                 mbRegSize, reinterpret_cast<uint8_t*>(&outputData), | 
 | 161 |                 &completionCode); | 
 | 162 |             if (tryWaking && isSleeping(libStatus, completionCode)) | 
 | 163 |             { | 
 | 164 |                 setWakeOnPECI(true); | 
 | 165 |                 tryWaking = false; | 
 | 166 |                 continue; | 
 | 167 |             } | 
 | 168 |             if (!checkPECIStatus(libStatus, completionCode)) | 
 | 169 |             { | 
 | 170 |                 throw PECIError("Failed to read mailbox reg"); | 
 | 171 |             } | 
 | 172 |             break; | 
 | 173 |         } | 
 | 174 |         return outputData; | 
 | 175 |     } | 
 | 176 |  | 
 | 177 |     /** | 
 | 178 |      * Send command on PCode OS Mailbox interface. | 
 | 179 |      * | 
 | 180 |      * @param[in]   command     Main command ID. | 
 | 181 |      * @param[in]   subCommand  Sub command ID. | 
 | 182 |      * @param[in]   inputData   Data to put in mailbox. Is always written, but | 
 | 183 |      *                          will be ignored by PCode if command is a | 
 | 184 |      *                          "getter". | 
 | 185 |      * @param[out]  responseCode    Optional parameter to receive the | 
 | 186 |      *                              mailbox-level response status. If null, a | 
 | 187 |      *                              PECIError will be thrown for error status. | 
 | 188 |      * | 
 | 189 |      * @return  Data returned in mailbox. Value is undefined if command is a | 
 | 190 |      *          "setter". | 
 | 191 |      */ | 
 | 192 |     uint32_t sendPECIOSMailboxCmd(uint8_t command, uint8_t subCommand, | 
 | 193 |                                   uint32_t inputData = 0, | 
 | 194 |                                   MailboxStatus* responseCode = nullptr) | 
 | 195 |     { | 
 | 196 |         // The simple mailbox algorithm just says to wait until the busy bit | 
 | 197 |         // is clear, but we'll give up after 10 tries. It's arbitrary but that's | 
 | 198 |         // quite long wall clock time. | 
 | 199 |         constexpr int mbRetries = 10; | 
 | 200 |         constexpr uint32_t mbBusyBit = bit(31); | 
 | 201 |  | 
 | 202 |         // Wait until RUN_BUSY == 0 | 
 | 203 |         int attempts = mbRetries; | 
 | 204 |         while ((rdMailboxReg(mbInterfaceReg) & mbBusyBit) != 0 && | 
 | 205 |                --attempts > 0) | 
 | 206 |             ; | 
 | 207 |         if (attempts == 0) | 
 | 208 |         { | 
 | 209 |             throw PECIError("OS Mailbox failed to become free"); | 
 | 210 |         } | 
 | 211 |  | 
 | 212 |         // Write required command specific input data to data register | 
 | 213 |         wrMailboxReg(mbDataReg, inputData); | 
 | 214 |  | 
 | 215 |         // Write required command specific command/sub-command values and set | 
 | 216 |         // RUN_BUSY bit in interface register. | 
 | 217 |         uint32_t interfaceReg = | 
 | 218 |             mbBusyBit | (static_cast<uint32_t>(subCommand) << 8) | command; | 
 | 219 |         wrMailboxReg(mbInterfaceReg, interfaceReg); | 
 | 220 |  | 
 | 221 |         // Wait until RUN_BUSY == 0 | 
 | 222 |         attempts = mbRetries; | 
 | 223 |         do | 
 | 224 |         { | 
 | 225 |             interfaceReg = rdMailboxReg(mbInterfaceReg); | 
 | 226 |         } while ((interfaceReg & mbBusyBit) != 0 && --attempts > 0); | 
 | 227 |         if (attempts == 0) | 
 | 228 |         { | 
 | 229 |             throw PECIError("OS Mailbox failed to return"); | 
 | 230 |         } | 
 | 231 |  | 
 | 232 |         // Read command return status or error code from interface register | 
 | 233 |         auto status = static_cast<MailboxStatus>(interfaceReg & 0xFF); | 
 | 234 |         if (responseCode != nullptr) | 
 | 235 |         { | 
 | 236 |             *responseCode = status; | 
 | 237 |         } | 
 | 238 |         else if (status != MailboxStatus::NoError) | 
 | 239 |         { | 
 | 240 |             throw PECIError(std::string("OS Mailbox returned with error: ") + | 
 | 241 |                             std::to_string(static_cast<int>(status))); | 
 | 242 |         } | 
 | 243 |  | 
 | 244 |         // Read command return data from the data register | 
 | 245 |         return rdMailboxReg(mbDataReg); | 
 | 246 |     } | 
 | 247 | }; | 
 | 248 |  | 
 | 249 | /** | 
 | 250 |  * Base class for set of PECI OS Mailbox commands. | 
 | 251 |  * Constructing it runs the command and stores the value for use by derived | 
 | 252 |  * class accessor methods. | 
 | 253 |  */ | 
 | 254 | template <uint8_t subcommand> | 
 | 255 | struct OsMailboxCommand | 
 | 256 | { | 
 | 257 |     enum ErrorPolicy | 
 | 258 |     { | 
 | 259 |         Throw, | 
 | 260 |         NoThrow | 
 | 261 |     }; | 
 | 262 |  | 
 | 263 |     uint32_t value; | 
 | 264 |     PECIManager::MailboxStatus status; | 
 | 265 |     /** | 
 | 266 |      * Construct the command object with required PECI address and up to 4 | 
 | 267 |      * optional 1-byte input data parameters. | 
 | 268 |      */ | 
 | 269 |     OsMailboxCommand(PECIManager& pm, uint8_t param1 = 0, uint8_t param2 = 0, | 
 | 270 |                      uint8_t param3 = 0, uint8_t param4 = 0) : | 
 | 271 |         OsMailboxCommand(pm, ErrorPolicy::Throw, param1, param2, param3, param4) | 
 | 272 |     {} | 
 | 273 |  | 
 | 274 |     OsMailboxCommand(PECIManager& pm, ErrorPolicy errorPolicy, | 
 | 275 |                      uint8_t param1 = 0, uint8_t param2 = 0, uint8_t param3 = 0, | 
 | 276 |                      uint8_t param4 = 0) | 
 | 277 |     { | 
 | 278 |         DEBUG_PRINT << "Running OS Mailbox command " | 
 | 279 |                     << static_cast<int>(subcommand) << '\n'; | 
 | 280 |         PECIManager::MailboxStatus* callStatus = | 
 | 281 |             errorPolicy == Throw ? nullptr : &status; | 
 | 282 |         uint32_t param = (static_cast<uint32_t>(param4) << 24) | | 
 | 283 |                          (static_cast<uint32_t>(param3) << 16) | | 
 | 284 |                          (static_cast<uint32_t>(param2) << 8) | param1; | 
 | 285 |         value = pm.sendPECIOSMailboxCmd(0x7F, subcommand, param, callStatus); | 
 | 286 |     } | 
 | 287 |  | 
 | 288 |     /** Return whether the mailbox status indicated success or not. */ | 
 | 289 |     bool success() const | 
 | 290 |     { | 
 | 291 |         return status == PECIManager::MailboxStatus::NoError; | 
 | 292 |     } | 
 | 293 | }; | 
 | 294 |  | 
 | 295 | /** | 
 | 296 |  * Macro to define a derived class accessor method. | 
 | 297 |  * | 
 | 298 |  * @param[in]   type    Return type of accessor method. | 
 | 299 |  * @param[in]   name    Name of accessor method. | 
 | 300 |  * @param[in]   hibit   Most significant bit of field to access. | 
 | 301 |  * @param[in]   lobit   Least significant bit of field to access. | 
 | 302 |  */ | 
 | 303 | #define FIELD(type, name, hibit, lobit)                                        \ | 
 | 304 |     type name() const                                                          \ | 
 | 305 |     {                                                                          \ | 
 | 306 |         return (value >> lobit) & (bit(hibit - lobit + 1) - 1);                \ | 
 | 307 |     } | 
 | 308 |  | 
 | 309 | struct GetLevelsInfo : OsMailboxCommand<0x0> | 
 | 310 | { | 
 | 311 |     using OsMailboxCommand::OsMailboxCommand; | 
 | 312 |     FIELD(bool, enabled, 31, 31) | 
 | 313 |     FIELD(bool, lock, 24, 24) | 
 | 314 |     FIELD(unsigned, currentConfigTdpLevel, 23, 16) | 
 | 315 |     FIELD(unsigned, configTdpLevels, 15, 8) | 
 | 316 |     FIELD(unsigned, version, 7, 0) | 
 | 317 | }; | 
 | 318 |  | 
 | 319 | struct GetConfigTdpControl : OsMailboxCommand<0x1> | 
 | 320 | { | 
 | 321 |     using OsMailboxCommand::OsMailboxCommand; | 
 | 322 |     FIELD(bool, pbfEnabled, 17, 17); | 
 | 323 |     FIELD(bool, factEnabled, 16, 16); | 
 | 324 |     FIELD(bool, pbfSupport, 1, 1); | 
 | 325 |     FIELD(bool, factSupport, 0, 0); | 
 | 326 | }; | 
 | 327 |  | 
 | 328 | struct SetConfigTdpControl : OsMailboxCommand<0x2> | 
 | 329 | { | 
 | 330 |     using OsMailboxCommand::OsMailboxCommand; | 
 | 331 | }; | 
 | 332 |  | 
 | 333 | struct GetTdpInfo : OsMailboxCommand<0x3> | 
 | 334 | { | 
 | 335 |     using OsMailboxCommand::OsMailboxCommand; | 
 | 336 |     FIELD(unsigned, tdpRatio, 23, 16); | 
 | 337 |     FIELD(unsigned, pkgTdp, 14, 0); | 
 | 338 | }; | 
 | 339 |  | 
 | 340 | struct GetCoreMask : OsMailboxCommand<0x6> | 
 | 341 | { | 
 | 342 |     using OsMailboxCommand::OsMailboxCommand; | 
 | 343 |     FIELD(uint32_t, coresMask, 31, 0); | 
 | 344 | }; | 
 | 345 |  | 
 | 346 | struct GetTurboLimitRatios : OsMailboxCommand<0x7> | 
 | 347 | { | 
 | 348 |     using OsMailboxCommand::OsMailboxCommand; | 
 | 349 | }; | 
 | 350 |  | 
 | 351 | struct SetLevel : OsMailboxCommand<0x8> | 
 | 352 | { | 
 | 353 |     using OsMailboxCommand::OsMailboxCommand; | 
 | 354 | }; | 
 | 355 |  | 
 | 356 | struct GetRatioInfo : OsMailboxCommand<0xC> | 
 | 357 | { | 
 | 358 |     using OsMailboxCommand::OsMailboxCommand; | 
 | 359 |     FIELD(unsigned, pm, 31, 24); | 
 | 360 |     FIELD(unsigned, pn, 23, 16); | 
 | 361 |     FIELD(unsigned, p1, 15, 8); | 
 | 362 |     FIELD(unsigned, p0, 7, 0); | 
 | 363 | }; | 
 | 364 |  | 
 | 365 | struct GetTjmaxInfo : OsMailboxCommand<0x5> | 
 | 366 | { | 
 | 367 |     using OsMailboxCommand::OsMailboxCommand; | 
 | 368 |     FIELD(unsigned, tProchot, 7, 0); | 
 | 369 | }; | 
 | 370 |  | 
 | 371 | struct PbfGetCoreMaskInfo : OsMailboxCommand<0x20> | 
 | 372 | { | 
 | 373 |     using OsMailboxCommand::OsMailboxCommand; | 
 | 374 |     FIELD(uint32_t, p1HiCoreMask, 31, 0); | 
 | 375 | }; | 
 | 376 |  | 
 | 377 | struct PbfGetP1HiP1LoInfo : OsMailboxCommand<0x21> | 
 | 378 | { | 
 | 379 |     using OsMailboxCommand::OsMailboxCommand; | 
 | 380 |     FIELD(unsigned, p1Hi, 15, 8); | 
 | 381 |     FIELD(unsigned, p1Lo, 7, 0); | 
 | 382 | }; | 
 | 383 |  | 
 | 384 | /** | 
 | 385 |  * Implementation of SSTInterface based on OS Mailbox interface supported on ICX | 
 | 386 |  * and SPR processors. | 
 | 387 |  * It's expected that an instance of this class will be created for each | 
 | 388 |  * "atomic" set of operations. | 
 | 389 |  */ | 
 | 390 | class SSTMailbox : public SSTInterface | 
 | 391 | { | 
 | 392 |   private: | 
 | 393 |     uint8_t address; | 
 | 394 |     CPUModel model; | 
 | 395 |     PECIManager pm; | 
 | 396 |  | 
 | 397 |     static constexpr int mhzPerRatio = 100; | 
 | 398 |  | 
 | 399 |   public: | 
 | 400 |     SSTMailbox(uint8_t _address, CPUModel _model) : | 
 | 401 |         address(_address), model(_model), | 
 | 402 |         pm(static_cast<uint8_t>(address), model) | 
 | 403 |     {} | 
 | 404 |     ~SSTMailbox() | 
 | 405 |     {} | 
 | 406 |  | 
 | 407 |     bool ready() override | 
 | 408 |     { | 
 | 409 |         return true; | 
 | 410 |     } | 
 | 411 |  | 
 | 412 |     bool supportsControl() override | 
 | 413 |     { | 
| Jonathan Doman | 949f634 | 2023-04-20 12:54:50 -0700 | [diff] [blame] | 414 |         switch (model) | 
 | 415 |         { | 
 | 416 |             case spr: | 
 | 417 |             case emr: | 
 | 418 |                 return true; | 
 | 419 |             default: | 
 | 420 |                 return false; | 
 | 421 |         } | 
| Jonathan Doman | 16a2ced | 2021-11-01 11:13:22 -0700 | [diff] [blame] | 422 |     } | 
 | 423 |  | 
 | 424 |     unsigned int currentLevel() override | 
 | 425 |     { | 
 | 426 |         return GetLevelsInfo(pm).currentConfigTdpLevel(); | 
 | 427 |     } | 
| Jonathan Doman | b4c3bcd | 2023-03-09 11:41:41 -0800 | [diff] [blame] | 428 |     unsigned int maxLevel() override | 
| Jonathan Doman | 16a2ced | 2021-11-01 11:13:22 -0700 | [diff] [blame] | 429 |     { | 
 | 430 |         return GetLevelsInfo(pm).configTdpLevels(); | 
 | 431 |     } | 
 | 432 |     bool ppEnabled() override | 
 | 433 |     { | 
 | 434 |         return GetLevelsInfo(pm).enabled(); | 
 | 435 |     } | 
 | 436 |  | 
 | 437 |     bool levelSupported(unsigned int level) override | 
 | 438 |     { | 
 | 439 |         GetConfigTdpControl tdpControl( | 
 | 440 |             pm, GetConfigTdpControl::ErrorPolicy::NoThrow, | 
 | 441 |             static_cast<uint8_t>(level)); | 
 | 442 |         return tdpControl.success(); | 
 | 443 |     } | 
 | 444 |     bool bfSupported(unsigned int level) override | 
 | 445 |     { | 
 | 446 |         return GetConfigTdpControl(pm, static_cast<uint8_t>(level)) | 
 | 447 |             .pbfSupport(); | 
 | 448 |     } | 
 | 449 |     bool tfSupported(unsigned int level) override | 
 | 450 |     { | 
 | 451 |         return GetConfigTdpControl(pm, static_cast<uint8_t>(level)) | 
 | 452 |             .factSupport(); | 
 | 453 |     } | 
 | 454 |     bool bfEnabled(unsigned int level) override | 
 | 455 |     { | 
 | 456 |         return GetConfigTdpControl(pm, static_cast<uint8_t>(level)) | 
 | 457 |             .pbfEnabled(); | 
 | 458 |     } | 
 | 459 |     bool tfEnabled(unsigned int level) override | 
 | 460 |     { | 
 | 461 |         return GetConfigTdpControl(pm, static_cast<uint8_t>(level)) | 
 | 462 |             .factEnabled(); | 
 | 463 |     } | 
 | 464 |     unsigned int tdp(unsigned int level) override | 
 | 465 |     { | 
 | 466 |         return GetTdpInfo(pm, static_cast<uint8_t>(level)).pkgTdp(); | 
 | 467 |     } | 
 | 468 |     unsigned int coreCount(unsigned int level) override | 
 | 469 |     { | 
 | 470 |         return enabledCoreList(level).size(); | 
 | 471 |     } | 
 | 472 |     std::vector<unsigned int> enabledCoreList(unsigned int level) override | 
 | 473 |     { | 
 | 474 |         uint64_t coreMaskLo = | 
 | 475 |             GetCoreMask(pm, static_cast<uint8_t>(level), 0).coresMask(); | 
 | 476 |         uint64_t coreMaskHi = | 
 | 477 |             GetCoreMask(pm, static_cast<uint8_t>(level), 1).coresMask(); | 
 | 478 |         std::bitset<64> coreMask = (coreMaskHi << 32 | coreMaskLo); | 
 | 479 |         return convertMaskToList(coreMask); | 
 | 480 |     } | 
 | 481 |     std::vector<TurboEntry> sseTurboProfile(unsigned int level) override | 
 | 482 |     { | 
 | 483 |         // Read the Turbo Ratio Limit Cores MSR which is used to generate the | 
 | 484 |         // Turbo Profile for each profile. This is a package scope MSR, so just | 
 | 485 |         // read thread 0. | 
 | 486 |         uint64_t trlCores; | 
 | 487 |         uint8_t cc; | 
 | 488 |         EPECIStatus status = peci_RdIAMSR(static_cast<uint8_t>(address), 0, | 
 | 489 |                                           0x1AE, &trlCores, &cc); | 
 | 490 |         if (!checkPECIStatus(status, cc)) | 
 | 491 |         { | 
 | 492 |             throw PECIError("Failed to read TRL MSR"); | 
 | 493 |         } | 
 | 494 |  | 
 | 495 |         std::vector<TurboEntry> turboSpeeds; | 
 | 496 |         uint64_t limitRatioLo = | 
 | 497 |             GetTurboLimitRatios(pm, static_cast<uint8_t>(level), 0, 0).value; | 
 | 498 |         uint64_t limitRatioHi = | 
 | 499 |             GetTurboLimitRatios(pm, static_cast<uint8_t>(level), 1, 0).value; | 
 | 500 |         uint64_t limitRatios = (limitRatioHi << 32) | limitRatioLo; | 
 | 501 |  | 
 | 502 |         constexpr int maxTFBuckets = 8; | 
 | 503 |         for (int i = 0; i < maxTFBuckets; ++i) | 
 | 504 |         { | 
 | 505 |             size_t bucketCount = trlCores & 0xFF; | 
 | 506 |             int bucketSpeed = limitRatios & 0xFF; | 
 | 507 |             if (bucketCount != 0 && bucketSpeed != 0) | 
 | 508 |             { | 
 | 509 |                 turboSpeeds.push_back({bucketSpeed * mhzPerRatio, bucketCount}); | 
 | 510 |             } | 
 | 511 |  | 
 | 512 |             trlCores >>= 8; | 
 | 513 |             limitRatios >>= 8; | 
 | 514 |         } | 
 | 515 |         return turboSpeeds; | 
 | 516 |     } | 
 | 517 |     unsigned int p1Freq(unsigned int level) override | 
 | 518 |     { | 
 | 519 |         return GetRatioInfo(pm, static_cast<uint8_t>(level)).p1() * mhzPerRatio; | 
 | 520 |     } | 
 | 521 |     unsigned int p0Freq(unsigned int level) override | 
 | 522 |     { | 
 | 523 |         return GetRatioInfo(pm, static_cast<uint8_t>(level)).p0() * mhzPerRatio; | 
 | 524 |     } | 
 | 525 |     unsigned int prochotTemp(unsigned int level) override | 
 | 526 |     { | 
 | 527 |         return GetTjmaxInfo(pm, static_cast<uint8_t>(level)).tProchot(); | 
 | 528 |     } | 
 | 529 |     std::vector<unsigned int> | 
 | 530 |         bfHighPriorityCoreList(unsigned int level) override | 
 | 531 |     { | 
 | 532 |         uint64_t coreMaskLo = | 
 | 533 |             PbfGetCoreMaskInfo(pm, static_cast<uint8_t>(level), 0) | 
 | 534 |                 .p1HiCoreMask(); | 
 | 535 |         uint64_t coreMaskHi = | 
 | 536 |             PbfGetCoreMaskInfo(pm, static_cast<uint8_t>(level), 1) | 
 | 537 |                 .p1HiCoreMask(); | 
 | 538 |         std::bitset<64> hiFreqCoreList = (coreMaskHi << 32) | coreMaskLo; | 
 | 539 |         return convertMaskToList(hiFreqCoreList); | 
 | 540 |     } | 
 | 541 |     unsigned int bfHighPriorityFreq(unsigned int level) override | 
 | 542 |     { | 
 | 543 |         return PbfGetP1HiP1LoInfo(pm, static_cast<uint8_t>(level)).p1Hi() * | 
 | 544 |                mhzPerRatio; | 
 | 545 |     } | 
 | 546 |     unsigned int bfLowPriorityFreq(unsigned int level) override | 
 | 547 |     { | 
 | 548 |         return PbfGetP1HiP1LoInfo(pm, static_cast<uint8_t>(level)).p1Lo() * | 
 | 549 |                mhzPerRatio; | 
 | 550 |     } | 
 | 551 |  | 
 | 552 |     void setBfEnabled(bool enable) override | 
 | 553 |     { | 
 | 554 |         GetConfigTdpControl getTDPControl(pm); | 
 | 555 |         bool tfEnabled = false; | 
 | 556 |         uint8_t param = (enable ? bit(1) : 0) | (tfEnabled ? bit(0) : 0); | 
 | 557 |         SetConfigTdpControl(pm, 0, 0, param); | 
 | 558 |     } | 
 | 559 |     void setTfEnabled(bool enable) override | 
 | 560 |     { | 
 | 561 |         // TODO: use cached BF value | 
 | 562 |         bool bfEnabled = false; | 
 | 563 |         uint8_t param = (bfEnabled ? bit(1) : 0) | (enable ? bit(0) : 0); | 
 | 564 |         SetConfigTdpControl(pm, 0, 0, param); | 
 | 565 |     } | 
 | 566 |     void setCurrentLevel(unsigned int level) override | 
 | 567 |     { | 
 | 568 |         SetLevel(pm, static_cast<uint8_t>(level)); | 
 | 569 |     } | 
 | 570 | }; | 
 | 571 |  | 
 | 572 | static std::unique_ptr<SSTInterface> createMailbox(uint8_t address, | 
 | 573 |                                                    CPUModel model) | 
 | 574 | { | 
 | 575 |     DEBUG_PRINT << "createMailbox\n"; | 
| Jonathan Doman | 949f634 | 2023-04-20 12:54:50 -0700 | [diff] [blame] | 576 |     switch (model) | 
| Jonathan Doman | 16a2ced | 2021-11-01 11:13:22 -0700 | [diff] [blame] | 577 |     { | 
| Jonathan Doman | 949f634 | 2023-04-20 12:54:50 -0700 | [diff] [blame] | 578 |         case icx: | 
 | 579 |         case icxd: | 
 | 580 |         case spr: | 
 | 581 |         case emr: | 
 | 582 |             return std::make_unique<SSTMailbox>(address, model); | 
 | 583 |         default: | 
 | 584 |             return nullptr; | 
| Jonathan Doman | 16a2ced | 2021-11-01 11:13:22 -0700 | [diff] [blame] | 585 |     } | 
| Jonathan Doman | 16a2ced | 2021-11-01 11:13:22 -0700 | [diff] [blame] | 586 | } | 
 | 587 |  | 
 | 588 | SSTProviderRegistration(createMailbox); | 
 | 589 |  | 
 | 590 | } // namespace sst | 
 | 591 | } // namespace cpu_info |