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
38CLOCK_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**/
58EFI_STATUS
59ConfigureClockGenerator (
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**/
215UINT8
216ReadClockGeneratorID (
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**/
262EFI_STATUS
263ConfigurePlatformClocks (
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
422static 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
430EFI_STATUS
431InstallPlatformClocksNotify (
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