1/** @file
2
3  Copyright (c) 2004  - 2014, Intel Corporation. All rights reserved.<BR>
4
5
6  This program and the accompanying materials are licensed and made available under
7
8  the terms and conditions of the BSD License that accompanies this distribution.
9
10  The full text of the license may be found at
11
12  http://opensource.org/licenses/bsd-license.php.
13
14
15
16  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
17
18  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19
20
21
22
23Module Name:
24
25
26  Dimm.c
27
28Abstract:
29
30  PPI for reading SPD modules on DIMMs.
31
32--*/
33
34
35//
36// Header Files
37//
38#include "Platformearlyinit.h"
39
40#define DIMM_SOCKETS     4  // Total number of DIMM sockets allowed on
41                            //   the platform
42#define DIMM_SEGMENTS    1  // Total number of Segments Per DIMM.
43#define MEMORY_CHANNELS  2  // Total number of memory channels
44                            //   populated on the system board
45//
46// Prototypes
47//
48
49EFI_STATUS
50EFIAPI
51GetDimmState (
52  IN      EFI_PEI_SERVICES        **PeiServices,
53  IN      PEI_PLATFORM_DIMM_PPI   *This,
54  IN      UINT8                   Dimm,
55  OUT     PEI_PLATFORM_DIMM_STATE *State
56  );
57
58EFI_STATUS
59EFIAPI
60SetDimmState (
61  IN      EFI_PEI_SERVICES        **PeiServices,
62  IN      PEI_PLATFORM_DIMM_PPI   *This,
63  IN      UINT8                   Dimm,
64  IN      PEI_PLATFORM_DIMM_STATE *State
65  );
66
67EFI_STATUS
68EFIAPI
69ReadSpd (
70  IN      EFI_PEI_SERVICES      **PeiServices,
71  IN      PEI_PLATFORM_DIMM_PPI *This,
72  IN      UINT8                 Dimm,
73  IN      UINT8                 Offset,
74  IN      UINTN                 Count,
75  IN OUT  UINT8                 *Buffer
76  );
77
78static PEI_PLATFORM_DIMM_PPI mGchDimmPpi = {
79  DIMM_SOCKETS,
80  DIMM_SEGMENTS,
81  MEMORY_CHANNELS,
82  GetDimmState,
83  SetDimmState,
84  ReadSpd
85};
86
87static EFI_PEI_PPI_DESCRIPTOR mPpiPlatformDimm = {
88  (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
89  &gPeiPlatformDimmPpiGuid,
90  &mGchDimmPpi
91};
92
93
94//
95// Functions
96//
97
98/**
99  This function returns the current state of a single DIMM.  Present indicates
100  that the DIMM slot is physically populated.  Disabled indicates that the DIMM
101  should not be used.
102
103  @param PeiServices   PEI services table pointer
104  @param This          PPI pointer
105  @param Dimm          DIMM to read from
106  @param State         Pointer to a return buffer to be updated with the current state
107                       of the DIMM
108
109  @retval EFI_SUCCESS         The function completed successfully.
110
111**/
112EFI_STATUS
113EFIAPI
114GetDimmState (
115  IN      EFI_PEI_SERVICES        **PeiServices,
116  IN      PEI_PLATFORM_DIMM_PPI   *This,
117  IN      UINT8                   Dimm,
118  OUT     PEI_PLATFORM_DIMM_STATE *State
119  )
120{
121  EFI_STATUS                    Status;
122  UINT8                         Buffer;
123
124  PEI_ASSERT (PeiServices, (Dimm < This->DimmSockets));
125
126  //
127  // A failure here does not necessarily mean that no DIMM is present.
128  // Read a single byte.  All we care about is the return status.
129  //
130  Status = ReadSpd (
131             PeiServices,
132             This,
133             Dimm,
134             0,
135             1,
136             &Buffer
137             );
138
139  if (EFI_ERROR (Status)) {
140    State->Present = 0;
141  } else {
142    State->Present = 1;
143  }
144
145  //
146  // BUGBUG: Update to check platform variable when it is available
147  //
148  State->Disabled = 0;
149  State->Reserved = 0;
150
151  return EFI_SUCCESS;
152}
153
154/**
155
156  This function updates the state of a single DIMM.
157
158  @param PeiServices          PEI services table pointer
159  @param This                 PPI pointer
160  @param Dimm                 DIMM to set state for
161  @param State                Pointer to the state information to set.
162
163  @retval EFI_SUCCESS         The function completed successfully.
164  @retval EFI_UNSUPPORTED     The function is not supported.
165
166**/
167EFI_STATUS
168EFIAPI
169SetDimmState (
170  IN      EFI_PEI_SERVICES        **PeiServices,
171  IN      PEI_PLATFORM_DIMM_PPI   *This,
172  IN      UINT8                   Dimm,
173  IN      PEI_PLATFORM_DIMM_STATE *State
174  )
175{
176  return EFI_UNSUPPORTED;
177}
178
179/**
180  This function reads SPD information from a DIMM.
181
182  PeiServices   PEI services table pointer
183  This          PPI pointer
184  Dimm          DIMM to read from
185  Offset        Offset in DIMM
186  Count         Number of bytes
187  Buffer        Return buffer
188
189  @param EFI_SUCCESS              The function completed successfully.
190  @param EFI_DEVICE_ERROR         The DIMM being accessed reported a device error,
191                                  does not have an SPD module, or is not installed in
192                                  the system.
193  @retval EFI_TIMEOUT             Time out trying to read the SPD module.
194  @retval EFI_INVALID_PARAMETER   A parameter was outside the legal limits.
195
196**/
197EFI_STATUS
198EFIAPI
199ReadSpd (
200  IN      EFI_PEI_SERVICES      **PeiServices,
201  IN      PEI_PLATFORM_DIMM_PPI *This,
202  IN      UINT8                 Dimm,
203  IN      UINT8                 Offset,
204  IN      UINTN                 Count,
205  IN OUT  UINT8                 *Buffer
206  )
207{
208  EFI_STATUS                Status;
209  PEI_SMBUS_PPI             *Smbus;
210  UINTN                     Index;
211  UINTN                     Index1;
212  EFI_SMBUS_DEVICE_ADDRESS  SlaveAddress;
213  EFI_SMBUS_DEVICE_COMMAND  Command;
214  UINTN                     Length;
215
216  Status = (**PeiServices).LocatePpi (
217                             PeiServices,
218                             &gPeiSmbusPpiGuid,   // GUID
219                             0,                   // INSTANCE
220                             NULL,                // EFI_PEI_PPI_DESCRIPTOR
221                             &Smbus               // PPI
222                             );
223  ASSERT_PEI_ERROR (PeiServices, Status);
224
225  switch (Dimm) {
226  case 0:
227    SlaveAddress.SmbusDeviceAddress = SMBUS_ADDR_CH_A_1 >> 1;
228    break;
229  case 1:
230    SlaveAddress.SmbusDeviceAddress = SMBUS_ADDR_CH_A_2 >> 1;
231    break;
232  case 2:
233    SlaveAddress.SmbusDeviceAddress = SMBUS_ADDR_CH_B_1 >> 1;
234    break;
235  case 3:
236    SlaveAddress.SmbusDeviceAddress = SMBUS_ADDR_CH_B_2 >> 1;
237    break;
238  default:
239    return EFI_INVALID_PARAMETER;
240  }
241
242  Index = Count % 4;
243  if (Index != 0) {
244    //
245    // read the first serveral bytes to speed up following reading
246    //
247    for (Index1 = 0; Index1 < Index; Index1++) {
248      Length = 1;
249      Command = Offset + Index1;
250      Status = Smbus->Execute (
251                        PeiServices,
252                        Smbus,
253                        SlaveAddress,
254                        Command,
255                        EfiSmbusReadByte,
256                        FALSE,
257                        &Length,
258                        &Buffer[Index1]
259                        );
260      if (EFI_ERROR(Status)) {
261        return Status;
262      }
263    }
264  }
265
266  //
267  // Now collect all the remaining bytes on 4 bytes block
268  //
269  for (; Index < Count; Index += 2) {
270    Command = Index + Offset;
271    Length = 2;
272    Status = Smbus->Execute (
273                      PeiServices,
274                      Smbus,
275                      SlaveAddress,
276                      Command,
277                      EfiSmbusReadWord,
278                      FALSE,
279                      &Length,
280                      &Buffer[Index]
281                      );
282    if (EFI_ERROR(Status)) {
283      return Status;
284    }
285
286    Index += 2;
287    Command = Index + Offset;
288    Length = 2;
289    Status = Smbus->Execute (
290                      PeiServices,
291                      Smbus,
292                      SlaveAddress,
293                      Command,
294                      EfiSmbusReadWord,
295                      FALSE,
296                      &Length,
297                      &Buffer[Index]
298                      );
299    if (EFI_ERROR(Status)) {
300      return Status;
301    }
302  }
303  return EFI_SUCCESS;
304}
305
306/**
307  This function initializes the PEIM.  It simply installs the DIMM PPI.
308
309  @param FfsHeader       Not used by this function
310  @param PeiServices     Pointer to PEI services table
311
312  @retval EFI_SUCCESS    The function completed successfully.
313
314**/
315EFI_STATUS
316EFIAPI
317PeimInitializeDimm (
318  IN EFI_PEI_SERVICES           **PeiServices,
319  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
320  IN VOID                       *SmbusPpi
321  )
322{
323  EFI_STATUS                    Status;
324
325  Status = (**PeiServices).InstallPpi (
326                             PeiServices,
327                             &mPpiPlatformDimm
328                             );
329  ASSERT_PEI_ERROR (PeiServices, Status);
330
331  return EFI_SUCCESS;
332}
333
334