55fb2c3b2296c31b9c5b828591ced957247dc660
[coreboot.git] / src / mainboard / amd / torpedo / dimmSpd.c
1 /*****************************************************************************
2  *
3  * Copyright (c) 2011, Advanced Micro Devices, Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *     * Redistributions of source code must retain the above copyright
9  *       notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above copyright
11  *       notice, this list of conditions and the following disclaimer in the
12  *       documentation and/or other materials provided with the distribution.
13  *     * Neither the name of Advanced Micro Devices, Inc. nor the names of
14  *       its contributors may be used to endorse or promote products derived
15  *       from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  ***************************************************************************/
29
30 /*----------------------------------------------------------------------------------------
31  *                             M O D U L E S    U S E D
32  *----------------------------------------------------------------------------------------
33  */
34
35 #include "Porting.h"
36 #include "AGESA.h"
37 #include "amdlib.h"
38 #include "dimmSpd.h"
39
40 /*----------------------------------------------------------------------------------------
41  *                   D E F I N I T I O N S    A N D    M A C R O S
42  *----------------------------------------------------------------------------------------
43  */
44 #define SMBUS_BASE_ADDR  0xB00
45 #define DIMENSION(array)(sizeof (array)/ sizeof (array [0]))
46
47 /*----------------------------------------------------------------------------------------
48  *                  T Y P E D E F S     A N D     S T R U C T U  R E S
49  *----------------------------------------------------------------------------------------
50  */
51
52 typedef struct _DIMM_INFO_SMBUS{
53   UINT8   SocketId;
54   UINT8   MemChannelId;
55   UINT8   DimmId;
56   UINT8   SmbusAddress;
57 } DIMM_INFO_SMBUS;
58 /*
59 * SPD address table - porting required
60 */
61 STATIC CONST DIMM_INFO_SMBUS SpdAddrLookup [] =
62 {
63   /* Socket, Channel, Dimm, Smbus */
64   {0, 0, 0, 0xA0},
65   {0, 1, 0, 0xA2}
66 };
67
68 /*----------------------------------------------------------------------------------------
69  *           P R O T O T Y P E S     O F     L O C A L     F U  N C T I O N S
70  *----------------------------------------------------------------------------------------
71  */
72
73 /*----------------------------------------------------------------------------------------
74  *                          E X P O R T E D    F U N C T I O N S
75  *----------------------------------------------------------------------------------------
76  */
77
78 /*---------------------------------------------------------------------------------------
79  *                          L O C A L    F U N C T I O N S
80  *---------------------------------------------------------------------------------------
81  */
82
83 STATIC
84 VOID
85 WritePmReg (
86   IN UINT8 Reg,
87   IN UINT8 Data
88   )
89 {
90    __outbyte (0xCD6, Reg);
91    __outbyte (0xCD7, Data);
92 }
93 STATIC
94 VOID
95 SetupFch (
96   IN UINT16
97   IN IoBase
98   )
99 {
100    WritePmReg (0x2D, IoBase >> 8);
101    WritePmReg (0x2C, IoBase | 1);
102    WritePmReg (0x29, 0x80);
103    WritePmReg (0x28, 0x61);
104    /* set SMBus clock to 400 KHz */
105    __outbyte (IoBase + 0x0E, 66000000 / 400000 / 4);
106 }
107
108 /*
109  *
110  * ReadSmbusByteData - read a single SPD byte from any offset
111  *
112  */
113
114 STATIC
115 AGESA_STATUS
116 ReadSmbusByteData (
117   IN UINT16 Iobase,
118   IN UINT8  Address,
119   OUT UINT8 *Buffer,
120   IN UINTN  Offset
121   )
122 {
123    UINTN  Status;
124    UINT64 Limit;
125
126    Address |= 1; // set read bit
127
128    __outbyte (Iobase + 0, 0xFF);                // clear error status
129    __outbyte (Iobase + 1, 0x1F);                // clear error status
130    __outbyte (Iobase + 3, Offset);              // offset in eeprom
131    __outbyte (Iobase + 4, Address);             // slave address and read bit
132    __outbyte (Iobase + 2, 0x48);                // read byte command
133
134    /* time limit to avoid hanging for unexpected error status (should never happen) */
135    Limit = __rdtsc () + 2000000000 / 10;
136    for (;;) {
137      Status = __inbyte (Iobase);
138      if (__rdtsc () > Limit) break;
139      if ((Status & 2) == 0) continue;               // SMBusInterrupt not set, keep waiting
140      if ((Status & 1) == 1) continue;               // HostBusy set, keep waiting
141      break;
142    }
143
144    Buffer [0] = __inbyte (Iobase + 5);
145    if (Status == 2) Status = 0;                      // check for done with no errors
146    return Status;
147    }
148
149 /*
150  *
151  * ReadSmbusByte - read a single SPD byte from the default offset
152  *                 this function is faster function readSmbusByteData
153  *
154  */
155
156 STATIC
157 AGESA_STATUS
158 ReadSmbusByte (
159   IN UINT16 Iobase,
160   IN UINT8  Address,
161   OUT UINT8 *Buffer
162   )
163 {
164   UINTN   Status;
165   UINT64  Limit;
166
167   __outbyte (Iobase + 0, 0xFF);                // clear error status
168   __outbyte (Iobase + 2, 0x44);                // read command
169
170   // time limit to avoid hanging for unexpected error status
171   Limit = __rdtsc () + 2000000000 / 10;
172   for (;;) {
173     Status = __inbyte (Iobase);
174     if (__rdtsc () > Limit) break;
175     if ((Status & 2) == 0) continue;               // SMBusInterrupt not set, keep waiting
176     if ((Status & 1) == 1) continue;               // HostBusy set, keep waiting
177     break;
178   }
179
180   Buffer [0] = __inbyte (Iobase + 5);
181   if (Status == 2) Status = 0;                      // check for done with no errors
182   return Status;
183 }
184
185 /*
186  *
187  * ReadSpd - Read one or more SPD bytes from a DIMM.
188  *           Start with offset zero and read sequentially.
189  *           Optimization relies on autoincrement to avoid
190  *           sending offset for every byte.
191  *           Reads 128 bytes in 7-8 ms at 400 KHz.
192  *
193  */
194
195 STATIC
196 AGESA_STATUS
197 ReadSpd (
198   IN UINT16 IoBase,
199   IN UINT8  SmbusSlaveAddress,
200   OUT UINT8 *Buffer,
201   IN UINTN  Count
202   )
203 {
204   UINTN Index, Status;
205
206   /* read the first byte using offset zero */
207   Status = ReadSmbusByteData (IoBase, SmbusSlaveAddress, Buffer, 0);
208   if (Status) return Status;
209
210   /* read the remaining bytes using auto-increment for speed */
211   for (Index = 1; Index < Count; Index++){
212     Status = ReadSmbusByte (IoBase, SmbusSlaveAddress, &Buffer [Index]);
213     if (Status) return Status;
214   }
215   return 0;
216 }
217
218 AGESA_STATUS
219 AmdMemoryReadSPD (
220   IN UINT32 Func,
221   IN UINT32 Data,
222   IN OUT AGESA_READ_SPD_PARAMS *SpdData
223   )
224 {
225    UINT8  SmBusAddress = 0;
226    UINTN  Index;
227    UINTN  MaxSocket = DIMENSION (SpdAddrLookup);
228    for (Index = 0; Index < MaxSocket; Index ++){
229      if ((SpdData->SocketId     == SpdAddrLookup[Index].SocketId)     &&
230          (SpdData->MemChannelId == SpdAddrLookup[Index].MemChannelId) &&
231          (SpdData->DimmId       == SpdAddrLookup[Index].DimmId)) {
232         SmBusAddress = SpdAddrLookup[Index].SmbusAddress;
233         break;
234       }
235    }
236
237
238    if (SmBusAddress == 0) return AGESA_ERROR;
239    SetupFch (SMBUS_BASE_ADDR);
240    return ReadSpd (SMBUS_BASE_ADDR, SmBusAddress, SpdData->Buffer, 128);
241 }