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