1 /** @file 2 Clock generator setting for multiplatform. 3 4 Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR> 5 6 This program and the accompanying materials are licensed and made available under 8 the terms and conditions of the BSD License that accompanies this distribution. 10 The full text of the license may be found at 12 http://opensource.org/licenses/bsd-license.php. 14 16 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 18 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 20 22 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. 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. 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 /** 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 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