1 /** @file 2 Clock generator setting for multiplatform. 3 4 Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR> 5 6 7 This program and the accompanying materials are licensed and made available under 8 9 the terms and conditions of the BSD License that accompanies this distribution. 10 11 The full text of the license may be found at 12 13 http://opensource.org/licenses/bsd-license.php. 14 15 16 17 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 18 19 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 20 21 22 23 24 **/ 25 26 #include <BoardClkGens.h> 27 #include <Guid/SetupVariable.h> 28 #include <Ppi/ReadOnlyVariable2.h> 29 #include <Library/BaseMemoryLib.h> 30 31 #ifndef __GNUC__ 32 #pragma optimize( "", off ) 33 #endif 34 35 #define CLKGEN_EN 1 36 #define EFI_DEBUG 1 37 38 CLOCK_GENERATOR_DETAILS mSupportedClockGeneratorTable[] = 39 { 40 { ClockGeneratorCk410, CK410_GENERATOR_ID , CK410_GENERATOR_SPREAD_SPECTRUM_BYTE, CK410_GENERATOR_SPREAD_SPECTRUM_BIT }, 41 { ClockGeneratorCk505, CK505_GENERATOR_ID , CK505_GENERATOR_SPREAD_SPECTRUM_BYTE, CK505_GENERATOR_SPREAD_SPECTRUM_BIT } 42 }; 43 44 /** 45 Configure the clock generator using the SMBUS PPI services. 46 47 This function performs a block write, and dumps debug information. 48 49 @param PeiServices General purpose services available to every PEIM. ConfigureClockGenerator(IN EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_SMBUS_PPI * SmbusPpi,IN CLOCK_GENERATOR_TYPE ClockType,IN UINT8 ClockAddress,IN UINTN ConfigurationTableLength,IN OUT UINT8 * ConfigurationTable)50 @param ClockType Clock generator's model name. 51 @param ClockAddress SMBUS address of clock generator. 52 @param ConfigurationTableLength Length of configuration table. 53 @param ConfigurationTable Pointer of configuration table. 54 55 @retval EFI_SUCCESS - Operation success. 56 57 **/ 58 EFI_STATUS 59 ConfigureClockGenerator ( 60 IN EFI_PEI_SERVICES **PeiServices, 61 IN EFI_PEI_SMBUS_PPI *SmbusPpi, 62 IN CLOCK_GENERATOR_TYPE ClockType, 63 IN UINT8 ClockAddress, 64 IN UINTN ConfigurationTableLength, 65 IN OUT UINT8 *ConfigurationTable 66 ) 67 { 68 69 EFI_STATUS Status; 70 EFI_SMBUS_DEVICE_ADDRESS SlaveAddress; 71 UINT8 Buffer[MAX_CLOCK_GENERATOR_BUFFER_LENGTH]; 72 UINTN Length; 73 EFI_SMBUS_DEVICE_COMMAND Command; 74 #if CLKGEN_CONFIG_EXTRA 75 UINT8 j; 76 #endif 77 78 // 79 // Verify input arguments 80 // 81 ASSERT_EFI_ERROR (ConfigurationTableLength >= 6); 82 ASSERT_EFI_ERROR (ConfigurationTableLength <= MAX_CLOCK_GENERATOR_BUFFER_LENGTH); 83 ASSERT_EFI_ERROR (ClockType < ClockGeneratorMax); 84 ASSERT_EFI_ERROR (ConfigurationTable != NULL); 85 86 // 87 // Read the clock generator 88 // 89 SlaveAddress.SmbusDeviceAddress = ClockAddress >> 1; 90 Length = sizeof (Buffer); 91 Command = 0; 92 Status = SmbusPpi->Execute ( 93 PeiServices, 94 SmbusPpi, 95 SlaveAddress, 96 Command, 97 EfiSmbusReadBlock, 98 FALSE, 99 &Length, 100 Buffer 101 ); 102 ASSERT_EFI_ERROR (Status); 103 104 #ifdef EFI_DEBUG 105 { 106 UINT8 i; 107 for (i = 0; i < sizeof (Buffer); i++) { 108 DEBUG((EFI_D_ERROR, "CK505 default Clock Generator Byte %d: %x\n", i, Buffer[i])); 109 } 110 #if CLKGEN_EN 111 for (i = 0; i < ConfigurationTableLength; i++) { 112 DEBUG((EFI_D_ERROR, "BIOS structure Clock Generator Byte %d: %x\n", i, ConfigurationTable[i])); 113 } 114 #endif 115 } 116 #endif 117 118 DEBUG((EFI_D_ERROR, "Expected Clock Generator ID is %x, expecting %x\n", mSupportedClockGeneratorTable[ClockType].ClockId,(Buffer[7]&0xF))); 119 120 // 121 // Program clock generator 122 // 123 Command = 0; 124 #if CLKGEN_EN 125 #if CLKGEN_CONFIG_EXTRA 126 for (j = 0; j < ConfigurationTableLength; j++) { 127 Buffer[j] = ConfigurationTable[j]; 128 } 129 130 Buffer[30] = 0x00; 131 132 Status = SmbusPpi->Execute ( 133 PeiServices, 134 SmbusPpi, 135 SlaveAddress, 136 Command, 137 EfiSmbusWriteBlock, 138 FALSE, 139 &Length, 140 Buffer 141 ); 142 #else 143 Status = SmbusPpi->Execute ( 144 PeiServices, 145 SmbusPpi, 146 SlaveAddress, 147 Command, 148 EfiSmbusWriteBlock, 149 FALSE, 150 &ConfigurationTableLength, 151 ConfigurationTable 152 ); 153 #endif // CLKGEN_CONFIG_EXTRA 154 #else 155 ConfigurationTable[4] = (ConfigurationTable[4] & 0x3) | (Buffer[4] & 0xFC); 156 Command = 4; 157 Length = 1; 158 Status = SmbusPpi->Execute ( 159 PeiServices, 160 SmbusPpi, 161 SlaveAddress, 162 Command, 163 EfiSmbusWriteBlock, 164 FALSE, 165 &Length, 166 &ConfigurationTable[4] 167 ); 168 #endif //CLKGEN_EN 169 ASSERT_EFI_ERROR (Status); 170 171 // 172 // Dump contents after write 173 // 174 #ifdef EFI_DEBUG 175 { 176 UINT8 i; 177 SlaveAddress.SmbusDeviceAddress = ClockAddress >> 1; 178 Length = sizeof (Buffer); 179 Command = 0; 180 Status = SmbusPpi->Execute ( 181 PeiServices, 182 SmbusPpi, 183 SlaveAddress, 184 Command, 185 EfiSmbusReadBlock, 186 FALSE, 187 &Length, 188 Buffer 189 ); 190 191 for (i = 0; i < ConfigurationTableLength; i++) { 192 DEBUG((EFI_D_ERROR, "Clock Generator Byte %d: %x\n", i, Buffer[i])); 193 } 194 } 195 #endif 196 197 return EFI_SUCCESS; 198 } 199 200 /** 201 Configure the clock generator using the SMBUS PPI services. 202 203 This function performs a block write, and dumps debug information. 204 205 @param PeiServices General purpose services available to every PEIM. 206 @param ClockType Clock generator's model name. ReadClockGeneratorID(IN EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_SMBUS_PPI * SmbusPpi,IN UINT8 ClockAddress)207 @param ClockAddress SMBUS address of clock generator. 208 @param ConfigurationTableLength Length of configuration table. 209 @param ConfigurationTable Pointer of configuration table. 210 211 212 @retval EFI_SUCCESS Operation success. 213 214 **/ 215 UINT8 216 ReadClockGeneratorID ( 217 IN EFI_PEI_SERVICES **PeiServices, 218 IN EFI_PEI_SMBUS_PPI *SmbusPpi, 219 IN UINT8 ClockAddress 220 ) 221 { 222 EFI_STATUS Status; 223 EFI_SMBUS_DEVICE_ADDRESS SlaveAddress; 224 UINT8 Buffer[MAX_CLOCK_GENERATOR_BUFFER_LENGTH]; 225 UINTN Length; 226 EFI_SMBUS_DEVICE_COMMAND Command; 227 228 // 229 // Read the clock generator 230 // 231 SlaveAddress.SmbusDeviceAddress = ClockAddress >> 1; 232 Length = sizeof (Buffer); 233 Command = 0; 234 Status = SmbusPpi->Execute ( 235 PeiServices, 236 SmbusPpi, 237 SlaveAddress, 238 Command, 239 EfiSmbusReadBlock, 240 FALSE, 241 &Length, 242 Buffer 243 ); 244 245 // 246 // Sanity check that the requested clock type is present in our supported clocks table 247 // 248 DEBUG((EFI_D_ERROR, "Expected Clock Generator ID is 0x%x\n", Buffer[7])); 249 250 return (Buffer[7]); 251 } 252 253 /** ConfigurePlatformClocks(IN EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_NOTIFY_DESCRIPTOR * NotifyDescriptor,IN VOID * SmbusPpi)254 Configure the clock generator to enable free-running operation. This keeps 255 the clocks from being stopped when the system enters C3 or C4. 256 257 @param None 258 259 @retval EFI_SUCCESS The function completed successfully. 260 261 **/ 262 EFI_STATUS 263 ConfigurePlatformClocks ( 264 IN EFI_PEI_SERVICES **PeiServices, 265 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, 266 IN VOID *SmbusPpi 267 ) 268 { 269 // 270 // Comment it out for now 271 // Not supported by Hybrid model. 272 // 273 EFI_STATUS Status; 274 UINT8 *ConfigurationTable; 275 276 CLOCK_GENERATOR_TYPE ClockType = ClockGeneratorCk505; 277 UINT8 ConfigurationTable_Desktop[] = CLOCK_GENERATOR_SETTINGS_DESKTOP; 278 UINT8 ConfigurationTable_Mobile[] = CLOCK_GENERATOR_SETTINGS_MOBILE; 279 UINT8 ConfigurationTable_Tablet[] = CLOCK_GENERATOR_SEETINGS_TABLET; 280 281 EFI_PLATFORM_INFO_HOB *PlatformInfoHob; 282 BOOLEAN EnableSpreadSpectrum; 283 UINT8 ClockGenID=0; 284 SYSTEM_CONFIGURATION SystemConfiguration; 285 286 UINTN Length; 287 EFI_SMBUS_DEVICE_COMMAND Command; 288 EFI_SMBUS_DEVICE_ADDRESS SlaveAddress; 289 UINT8 Data; 290 291 UINT8 ClockAddress = CLOCK_GENERATOR_ADDRESS; 292 UINTN VariableSize; 293 EFI_PEI_READ_ONLY_VARIABLE2_PPI *Variable; 294 295 // 296 // Obtain Platform Info from HOB. 297 // 298 Status = GetPlatformInfoHob ((CONST EFI_PEI_SERVICES **) PeiServices, &PlatformInfoHob); 299 ASSERT_EFI_ERROR (Status); 300 301 DEBUG((EFI_D_ERROR, "PlatformInfo protocol is working in ConfigurePlatformClocks()...%x\n",PlatformInfoHob->PlatformFlavor)); 302 303 // 304 // Locate SMBUS PPI 305 // 306 Status = (**PeiServices).LocatePpi ( 307 (CONST EFI_PEI_SERVICES **) PeiServices, 308 &gEfiPeiSmbusPpiGuid, 309 0, 310 NULL, 311 &SmbusPpi 312 ); 313 ASSERT_EFI_ERROR (Status); 314 315 Data = 0; 316 SlaveAddress.SmbusDeviceAddress = ClockAddress >> 1; 317 Length = 1; 318 Command = 0x87; //Control Register 7 Vendor ID Check 319 Status = ((EFI_PEI_SMBUS_PPI *) SmbusPpi)->Execute ( 320 PeiServices, 321 SmbusPpi, 322 SlaveAddress, 323 Command, 324 EfiSmbusReadByte, 325 FALSE, 326 &Length, 327 &Data 328 ); 329 330 if (EFI_ERROR (Status) || ((Data & 0x0F) != CK505_GENERATOR_ID)) { 331 DEBUG((EFI_D_ERROR, "Clock Generator CK505 Not Present, vendor ID on board is %x\n",(Data & 0x0F))); 332 return EFI_SUCCESS; 333 } 334 ClockGenID = Data & 0x0F; 335 336 EnableSpreadSpectrum = FALSE; 337 VariableSize = sizeof (SYSTEM_CONFIGURATION); 338 ZeroMem (&SystemConfiguration, sizeof (SYSTEM_CONFIGURATION)); 339 340 Status = (*PeiServices)->LocatePpi ( 341 (CONST EFI_PEI_SERVICES **) PeiServices, 342 &gEfiPeiReadOnlyVariable2PpiGuid, 343 0, 344 NULL, 345 (VOID **) &Variable 346 ); 347 // 348 // Use normal setup default from NVRAM variable, 349 // the Platform Mode (manufacturing/safe/normal) is handle in PeiGetVariable. 350 // 351 VariableSize = sizeof(SYSTEM_CONFIGURATION); 352 Status = Variable->GetVariable (Variable, 353 L"Setup", 354 &gEfiSetupVariableGuid, 355 NULL, 356 &VariableSize, 357 &SystemConfiguration); 358 if (EFI_ERROR (Status) || VariableSize != sizeof(SYSTEM_CONFIGURATION)) { 359 //The setup variable is corrupted 360 VariableSize = sizeof(SYSTEM_CONFIGURATION); 361 Status = Variable->GetVariable(Variable, 362 L"SetupRecovery", 363 &gEfiSetupVariableGuid, 364 NULL, 365 &VariableSize, 366 &SystemConfiguration 367 ); 368 ASSERT_EFI_ERROR (Status); 369 } 370 if(!EFI_ERROR (Status)){ 371 EnableSpreadSpectrum = SystemConfiguration.EnableClockSpreadSpec; 372 } 373 374 // 375 // Perform platform-specific intialization dependent upon Board ID: 376 // 377 DEBUG((EFI_D_ERROR, "board id is %x, platform id is %x\n",PlatformInfoHob->BoardId,PlatformInfoHob->PlatformFlavor)); 378 379 380 switch (PlatformInfoHob->BoardId) { 381 case BOARD_ID_MINNOW2: 382 case BOARD_ID_MINNOW2_TURBOT: 383 default: 384 switch(PlatformInfoHob->PlatformFlavor) { 385 case FlavorTablet: 386 ConfigurationTable = ConfigurationTable_Tablet; 387 Length = sizeof (ConfigurationTable_Tablet); 388 break; 389 case FlavorMobile: 390 ConfigurationTable = ConfigurationTable_Mobile; 391 Length = sizeof (ConfigurationTable_Mobile); 392 break; 393 case FlavorDesktop: 394 default: 395 ConfigurationTable = ConfigurationTable_Desktop; 396 Length = sizeof (ConfigurationTable_Desktop); 397 break; 398 } 399 break; 400 } 401 402 // 403 // Perform common clock initialization: 404 // 405 // Program Spread Spectrum function. 406 // 407 if (EnableSpreadSpectrum) 408 { 409 ConfigurationTable[mSupportedClockGeneratorTable[ClockType].SpreadSpectrumByteOffset] |= mSupportedClockGeneratorTable[ClockType].SpreadSpectrumBitOffset; 410 } else { 411 ConfigurationTable[mSupportedClockGeneratorTable[ClockType].SpreadSpectrumByteOffset] &= ~(mSupportedClockGeneratorTable[ClockType].SpreadSpectrumBitOffset); 412 } 413 414 415 #if CLKGEN_EN 416 Status = ConfigureClockGenerator (PeiServices, SmbusPpi, ClockType, ClockAddress, Length, ConfigurationTable); 417 ASSERT_EFI_ERROR (Status); 418 #endif // CLKGEN_EN 419 return EFI_SUCCESS; 420 } 421 InstallPlatformClocksNotify(IN CONST EFI_PEI_SERVICES ** PeiServices)422 static EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList[] = { 423 { 424 EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, 425 &gEfiPeiSmbusPpiGuid, 426 ConfigurePlatformClocks 427 } 428 }; 429 430 EFI_STATUS 431 InstallPlatformClocksNotify ( 432 IN CONST EFI_PEI_SERVICES **PeiServices 433 ) 434 { 435 EFI_STATUS Status; 436 437 DEBUG ((EFI_D_INFO, "InstallPlatformClocksNotify()...\n")); 438 439 Status = (*PeiServices)->NotifyPpi(PeiServices, &mNotifyList[0]); 440 ASSERT_EFI_ERROR (Status); 441 return EFI_SUCCESS; 442 443 } 444 445 #ifndef __GNUC__ 446 #pragma optimize( "", on ) 447 #endif 448