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