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