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 (ConfigurationTableLength >= 6);
82 ASSERT (ConfigurationTableLength <= MAX_CLOCK_GENERATOR_BUFFER_LENGTH);
83 ASSERT (ClockType < ClockGeneratorMax);
84 ASSERT (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_SMBUS_DEVICE_ADDRESS SlaveAddress;
223 UINT8 Buffer[MAX_CLOCK_GENERATOR_BUFFER_LENGTH];
224 UINTN Length;
225 EFI_SMBUS_DEVICE_COMMAND Command;
226
227 //
228 // Read the clock generator
229 //
230 SlaveAddress.SmbusDeviceAddress = ClockAddress >> 1;
231 Length = sizeof (Buffer);
232 Command = 0;
233 SmbusPpi->Execute (
234 PeiServices,
235 SmbusPpi,
236 SlaveAddress,
237 Command,
238 EfiSmbusReadBlock,
239 FALSE,
240 &Length,
241 Buffer
242 );
243
244 //
245 // Sanity check that the requested clock type is present in our supported clocks table
246 //
247 DEBUG((EFI_D_ERROR, "Expected Clock Generator ID is 0x%x\n", Buffer[7]));
248
249 return (Buffer[7]);
250 }
251
252 /**
ConfigurePlatformClocks(IN EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_NOTIFY_DESCRIPTOR * NotifyDescriptor,IN VOID * SmbusPpi)253 Configure the clock generator to enable free-running operation. This keeps
254 the clocks from being stopped when the system enters C3 or C4.
255
256 @param None
257
258 @retval EFI_SUCCESS The function completed successfully.
259
260 **/
261 EFI_STATUS
262 ConfigurePlatformClocks (
263 IN EFI_PEI_SERVICES **PeiServices,
264 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
265 IN VOID *SmbusPpi
266 )
267 {
268 //
269 // Comment it out for now
270 // Not supported by Hybrid model.
271 //
272 EFI_STATUS Status;
273 UINT8 *ConfigurationTable;
274
275 CLOCK_GENERATOR_TYPE ClockType = ClockGeneratorCk505;
276 UINT8 ConfigurationTable_Desktop[] = CLOCK_GENERATOR_SETTINGS_DESKTOP;
277 UINT8 ConfigurationTable_Mobile[] = CLOCK_GENERATOR_SETTINGS_MOBILE;
278 UINT8 ConfigurationTable_Tablet[] = CLOCK_GENERATOR_SEETINGS_TABLET;
279
280 EFI_PLATFORM_INFO_HOB *PlatformInfoHob;
281 BOOLEAN EnableSpreadSpectrum;
282 SYSTEM_CONFIGURATION SystemConfiguration;
283
284 UINTN Length;
285 EFI_SMBUS_DEVICE_COMMAND Command;
286 EFI_SMBUS_DEVICE_ADDRESS SlaveAddress;
287 UINT8 Data;
288
289 UINT8 ClockAddress = CLOCK_GENERATOR_ADDRESS;
290 UINTN VariableSize;
291 EFI_PEI_READ_ONLY_VARIABLE2_PPI *Variable;
292
293 //
294 // Obtain Platform Info from HOB.
295 //
296 Status = GetPlatformInfoHob ((CONST EFI_PEI_SERVICES **) PeiServices, &PlatformInfoHob);
297 ASSERT_EFI_ERROR (Status);
298
299 DEBUG((EFI_D_ERROR, "PlatformInfo protocol is working in ConfigurePlatformClocks()...%x\n",PlatformInfoHob->PlatformFlavor));
300
301 //
302 // Locate SMBUS PPI
303 //
304 Status = (**PeiServices).LocatePpi (
305 (CONST EFI_PEI_SERVICES **) PeiServices,
306 &gEfiPeiSmbusPpiGuid,
307 0,
308 NULL,
309 &SmbusPpi
310 );
311 ASSERT_EFI_ERROR (Status);
312
313 Data = 0;
314 SlaveAddress.SmbusDeviceAddress = ClockAddress >> 1;
315 Length = 1;
316 Command = 0x87; //Control Register 7 Vendor ID Check
317 Status = ((EFI_PEI_SMBUS_PPI *) SmbusPpi)->Execute (
318 PeiServices,
319 SmbusPpi,
320 SlaveAddress,
321 Command,
322 EfiSmbusReadByte,
323 FALSE,
324 &Length,
325 &Data
326 );
327
328 if (EFI_ERROR (Status) || ((Data & 0x0F) != CK505_GENERATOR_ID)) {
329 DEBUG((EFI_D_ERROR, "Clock Generator CK505 Not Present, vendor ID on board is %x\n",(Data & 0x0F)));
330 return EFI_SUCCESS;
331 }
332
333 EnableSpreadSpectrum = FALSE;
334 VariableSize = sizeof (SYSTEM_CONFIGURATION);
335 ZeroMem (&SystemConfiguration, sizeof (SYSTEM_CONFIGURATION));
336
337 Status = (*PeiServices)->LocatePpi (
338 (CONST EFI_PEI_SERVICES **) PeiServices,
339 &gEfiPeiReadOnlyVariable2PpiGuid,
340 0,
341 NULL,
342 (VOID **) &Variable
343 );
344 //
345 // Use normal setup default from NVRAM variable,
346 // the Platform Mode (manufacturing/safe/normal) is handle in PeiGetVariable.
347 //
348 VariableSize = sizeof(SYSTEM_CONFIGURATION);
349 Status = Variable->GetVariable (Variable,
350 L"Setup",
351 &gEfiSetupVariableGuid,
352 NULL,
353 &VariableSize,
354 &SystemConfiguration);
355 if (EFI_ERROR (Status) || VariableSize != sizeof(SYSTEM_CONFIGURATION)) {
356 //The setup variable is corrupted
357 VariableSize = sizeof(SYSTEM_CONFIGURATION);
358 Status = Variable->GetVariable(Variable,
359 L"SetupRecovery",
360 &gEfiSetupVariableGuid,
361 NULL,
362 &VariableSize,
363 &SystemConfiguration
364 );
365 ASSERT_EFI_ERROR (Status);
366 }
367 if(!EFI_ERROR (Status)){
368 EnableSpreadSpectrum = SystemConfiguration.EnableClockSpreadSpec;
369 }
370
371 //
372 // Perform platform-specific intialization dependent upon Board ID:
373 //
374 DEBUG((EFI_D_ERROR, "board id is %x, platform id is %x\n",PlatformInfoHob->BoardId,PlatformInfoHob->PlatformFlavor));
375
376
377 switch (PlatformInfoHob->BoardId) {
378 case BOARD_ID_MINNOW2:
379 case BOARD_ID_MINNOW2_TURBOT:
380 default:
381 switch(PlatformInfoHob->PlatformFlavor) {
382 case FlavorTablet:
383 ConfigurationTable = ConfigurationTable_Tablet;
384 Length = sizeof (ConfigurationTable_Tablet);
385 break;
386 case FlavorMobile:
387 ConfigurationTable = ConfigurationTable_Mobile;
388 Length = sizeof (ConfigurationTable_Mobile);
389 break;
390 case FlavorDesktop:
391 default:
392 ConfigurationTable = ConfigurationTable_Desktop;
393 Length = sizeof (ConfigurationTable_Desktop);
394 break;
395 }
396 break;
397 }
398
399 //
400 // Perform common clock initialization:
401 //
402 // Program Spread Spectrum function.
403 //
404 if (EnableSpreadSpectrum)
405 {
406 ConfigurationTable[mSupportedClockGeneratorTable[ClockType].SpreadSpectrumByteOffset] |= mSupportedClockGeneratorTable[ClockType].SpreadSpectrumBitOffset;
407 } else {
408 ConfigurationTable[mSupportedClockGeneratorTable[ClockType].SpreadSpectrumByteOffset] &= ~(mSupportedClockGeneratorTable[ClockType].SpreadSpectrumBitOffset);
409 }
410
411
412 #if CLKGEN_EN
413 Status = ConfigureClockGenerator (PeiServices, SmbusPpi, ClockType, ClockAddress, Length, ConfigurationTable);
414 ASSERT_EFI_ERROR (Status);
415 #endif // CLKGEN_EN
416 return EFI_SUCCESS;
417 }
418
InstallPlatformClocksNotify(IN CONST EFI_PEI_SERVICES ** PeiServices)419 static EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList[] = {
420 {
421 EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
422 &gEfiPeiSmbusPpiGuid,
423 ConfigurePlatformClocks
424 }
425 };
426
427 EFI_STATUS
428 InstallPlatformClocksNotify (
429 IN CONST EFI_PEI_SERVICES **PeiServices
430 )
431 {
432 EFI_STATUS Status;
433
434 DEBUG ((EFI_D_INFO, "InstallPlatformClocksNotify()...\n"));
435
436 Status = (*PeiServices)->NotifyPpi(PeiServices, &mNotifyList[0]);
437 ASSERT_EFI_ERROR (Status);
438 return EFI_SUCCESS;
439
440 }
441
442 #ifndef __GNUC__
443 #pragma optimize( "", on )
444 #endif
445