Cleanup Persimmon mainboard whitespace.
[coreboot.git] / src / mainboard / amd / persimmon / dimmSpd.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2011 Advanced Micro Devices, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA    02110-1301 USA
18  */
19
20 #include "Porting.h"
21 #include "AGESA.h"
22 #include "amdlib.h"
23
24 AGESA_STATUS AmdMemoryReadSPD (UINT32 unused1, UINT32 unused2, AGESA_READ_SPD_PARAMS *info);
25 #define DIMENSION(array)(sizeof (array)/ sizeof (array [0]))
26
27 /*#pragma optimize ("", off) // for source level debug
28 *---------------------------------------------------------------------------
29 *
30 * SPD address table - porting required
31 */
32
33 static const UINT8 spdAddressLookup [2] [2] [4] =       // socket, channel, dimm
34 {
35 // socket 0
36         {
37                 {0xA0, 0xA2},   // channel 0 dimms
38                 {0x00, 0x00},   // channel 1 dimms
39         },
40         // socket 1
41         {
42                 {0x00, 0x00},   // channel 0 dimms
43                 {0x00, 0x00},   // channel 1 dimms
44         },
45 };
46
47 /*-----------------------------------------------------------------------------
48  *
49  * readSmbusByteData - read a single SPD byte from any offset
50  */
51
52 static int readSmbusByteData (int iobase, int address, char *buffer, int offset)
53 {
54         unsigned int status;
55         UINT64 limit;
56
57         address |= 1; // set read bit
58
59         __outbyte (iobase + 0, 0xFF);                           // clear error status
60         __outbyte (iobase + 1, 0x1F);                           // clear error status
61         __outbyte (iobase + 3, offset);                         // offset in eeprom
62         __outbyte (iobase + 4, address);                        // slave address and read bit
63         __outbyte (iobase + 2, 0x48);                           // read byte command
64
65         // time limit to avoid hanging for unexpected error status (should never happen)
66         limit = __rdtsc () + 2000000000 / 10;
67         for (;;) {
68                 status = __inbyte (iobase);
69                 if (__rdtsc () > limit) break;
70                 if ((status & 2) == 0) continue;                // SMBusInterrupt not set, keep waiting
71                 if ((status & 1) == 1) continue;                // HostBusy set, keep waiting
72                 break;
73         }
74
75         buffer [0] = __inbyte (iobase + 5);
76         if (status == 2) status = 0;                            // check for done with no errors
77         return status;
78 }
79
80 /*-----------------------------------------------------------------------------
81  *
82  * readSmbusByte - read a single SPD byte from the default offset
83  *                               this function is faster function readSmbusByteData
84  */
85
86 static int readSmbusByte (int iobase, int address, char *buffer)
87 {
88         unsigned int status;
89         UINT64 limit;
90
91         __outbyte (iobase + 0, 0xFF);                           // clear error status
92         __outbyte (iobase + 2, 0x44);                           // read command
93
94         // time limit to avoid hanging for unexpected error status
95         limit = __rdtsc () + 2000000000 / 10;
96         for (;;) {
97                 status = __inbyte (iobase);
98                 if (__rdtsc () > limit) break;
99                 if ((status & 2) == 0) continue;                // SMBusInterrupt not set, keep waiting
100                 if ((status & 1) == 1) continue;                // HostBusy set, keep waiting
101                 break;
102         }
103
104         buffer [0] = __inbyte (iobase + 5);
105         if (status == 2) status = 0;                            // check for done with no errors
106         return status;
107 }
108
109 /*---------------------------------------------------------------------------
110  *
111  * readspd - Read one or more SPD bytes from a DIMM.
112  *                      Start with offset zero and read sequentially.
113  *                      Optimization relies on autoincrement to avoid
114  *                      sending offset for every byte.
115  *                      Reads 128 bytes in 7-8 ms at 400 KHz.
116  */
117
118 static int readspd (int iobase, int SmbusSlaveAddress, char *buffer, int count)
119 {
120         int index, error;
121
122         /* read the first byte using offset zero */
123         error = readSmbusByteData (iobase, SmbusSlaveAddress, buffer, 0);
124         if (error) return error;
125
126         /* read the remaining bytes using auto-increment for speed */
127         for (index = 1; index < count; index++) {
128                 error = readSmbusByte (iobase, SmbusSlaveAddress, &buffer [index]);
129                 if (error) return error;
130         }
131
132         return 0;
133 }
134
135 static void writePmReg (int reg, int data)
136         {
137         __outbyte (0xCD6, reg);
138         __outbyte (0xCD7, data);
139         }
140
141 static void setupFch (int ioBase)
142 {
143         writePmReg (0x2D, ioBase >> 8);
144         writePmReg (0x2C, ioBase | 1);
145         writePmReg (0x29, 0x80);
146         writePmReg (0x28, 0x61);
147         __outbyte (ioBase + 0x0E, 66000000 / 400000 / 4); // set SMBus clock to 400 KHz
148 }
149
150 AGESA_STATUS AmdMemoryReadSPD (UINT32 unused1, UINT32 unused2, AGESA_READ_SPD_PARAMS *info)
151 {
152         int spdAddress, ioBase;
153
154         if (info->SocketId       >= DIMENSION (spdAddressLookup         )) return AGESA_ERROR;
155         if (info->MemChannelId >= DIMENSION (spdAddressLookup[0]        )) return AGESA_ERROR;
156         if (info->DimmId                >= DIMENSION (spdAddressLookup[0][0])) return AGESA_ERROR;
157
158         spdAddress = spdAddressLookup [info->SocketId] [info->MemChannelId] [info->DimmId];
159         if (spdAddress == 0) return AGESA_ERROR;
160         ioBase = 0xB00;
161         setupFch (ioBase);
162         return readspd (ioBase, spdAddress, (void *) info->Buffer, 128);
163 }