Add constants for fast path resume copying
[coreboot.git] / src / mainboard / supermicro / h8qgi / dimmSpd.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2011 - 2012 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 #include <arch/io.h>
24 #include <arch/romcc_io.h>
25 #include <device/pci_ids.h>
26
27 AGESA_STATUS AmdMemoryReadSPD (UINT32 unused1, UINT32 unused2, AGESA_READ_SPD_PARAMS *info);
28 #define DIMENSION(array)(sizeof (array)/ sizeof (array [0]))
29
30 /* SP5100 GPIO 53-56 contoled by SMBUS PCI_Reg 0x52 */
31 #define SP5100_GPIO53_56        0x52
32
33 /**
34  * TODO not support all GPIO yet
35  * @param reg -GPIO Cntrl Register
36  * @param out -GPIO bitmap
37  * @param out -GPIO enable bitmap
38  * @return  old setting
39  */
40 static u8 sp5100_set_gpio(u8 reg, u8 out, u8 enable)
41 {
42         u8 value, ret;
43         device_t sm_dev = PCI_DEV(0, 0x14, 0); //SMBUS
44
45         value = pci_read_config8(sm_dev, reg);
46         ret = value;
47         value &= ~(enable);
48         value |= out;
49         value &= ~(enable << 4);
50         pci_write_config8(sm_dev, reg, value);
51
52         return ret;
53 }
54
55 static void sp5100_restore_gpio(u8 reg, u8 value)
56 {
57         device_t sm_dev = PCI_DEV(0, 0x14, 0);
58         pci_write_config8(sm_dev, reg, value);
59 }
60
61 /*-----------------------------------------------------------------------------
62  *
63  * SPD address table - porting required
64  */
65 static const UINT8 spdAddressLookup [8] [4] [2] = { // socket, channel, dimm
66         /* socket 0 */
67         {
68                 {0xAC, 0xAE},
69                 {0xA8, 0xAA},
70                 {0xA4, 0xA6},
71                 {0xA0, 0xA2},
72         },
73         /* socket 1 */
74         {
75                 {0xAC, 0xAE},
76                 {0xA8, 0xAA},
77                 {0xA4, 0xA6},
78                 {0xA0, 0xA2},
79         },
80         /* socket 2 */
81         {
82                 {0xAC, 0xAE},
83                 {0xA8, 0xAA},
84                 {0xA4, 0xA6},
85                 {0xA0, 0xA2},
86         },
87         /* socket 3 */
88         {
89                 {0xAC, 0xAE},
90                 {0xA8, 0xAA},
91                 {0xA4, 0xA6},
92                 {0xA0, 0xA2},
93         },
94 };
95
96 /*-----------------------------------------------------------------------------
97  *
98  * readSmbusByteData - read a single SPD byte from any offset
99  */
100
101 static int readSmbusByteData (int iobase, int address, char *buffer, int offset)
102 {
103         unsigned int status;
104         UINT64 limit;
105
106         address |= 1; // set read bit
107
108         outb(0xFF, iobase + 0);                // clear error status
109         outb(0x1F, iobase + 1);                // clear error status
110         outb(offset, iobase + 3);              // offset in eeprom
111         outb(address, iobase + 4);             // slave address and read bit
112         outb(0x48, iobase + 2);                // read byte command
113
114         // time limit to avoid hanging for unexpected error status (should never happen)
115         limit = __rdtsc () + 2000000000 / 10;
116         for (;;)
117         {
118                 status = inb(iobase);
119                 if (__rdtsc () > limit) break;
120                 if ((status & 2) == 0) continue;               // SMBusInterrupt not set, keep waiting
121                 if ((status & 1) == 1) continue;               // HostBusy set, keep waiting
122                 break;
123         }
124
125         buffer [0] = inb(iobase + 5);
126         if (status == 2) status = 0;                      // check for done with no errors
127         return status;
128 }
129
130 /*-----------------------------------------------------------------------------
131  *
132  * readSmbusByte - read a single SPD byte from the default offset
133  *                 this function is faster function readSmbusByteData
134  */
135
136 static int readSmbusByte (int iobase, int address, char *buffer)
137 {
138         unsigned int status;
139         UINT64 limit;
140
141         outb(0xFF, iobase + 0);                // clear error status
142         outb(0x44, iobase + 2);                // read command
143
144         // time limit to avoid hanging for unexpected error status
145         limit = __rdtsc () + 2000000000 / 10;
146         for (;;)
147         {
148                 status = inb(iobase);
149                 if (__rdtsc () > limit) break;
150                 if ((status & 2) == 0) continue;               // SMBusInterrupt not set, keep waiting
151                 if ((status & 1) == 1) continue;               // HostBusy set, keep waiting
152                 break;
153         }
154
155         buffer [0] = inb(iobase + 5);
156         if (status == 2) status = 0;                      // check for done with no errors
157         return status;
158 }
159
160 /*---------------------------------------------------------------------------
161  *
162  * readspd - Read one or more SPD bytes from a DIMM.
163  *           Start with offset zero and read sequentially.
164  *           Optimization relies on autoincrement to avoid
165  *           sending offset for every byte.
166  *          Reads 128 bytes in 7-8 ms at 400 KHz.
167  */
168
169 static int readspd (int iobase, int SmbusSlaveAddress, char *buffer, int count)
170 {
171         int index, error;
172
173         /* read the first byte using offset zero */
174         error = readSmbusByteData (iobase, SmbusSlaveAddress, buffer, 0);
175         if (error) {
176                 return error;
177         }
178
179         /* read the remaining bytes using auto-increment for speed */
180         for (index = 1; index < count; index++)
181         {
182                 error = readSmbusByte (iobase, SmbusSlaveAddress, buffer + index);
183                 if (error)
184                         return error;
185         }
186
187         return 0;
188 }
189
190 static void setupFch (int ioBase)
191 {
192         outb(66000000 / 400000 / 4, ioBase + 0x0E); /* set SMBus clock to 400 KHz */
193 }
194
195 AGESA_STATUS AmdMemoryReadSPD (UINT32 unused1, UINT32 unused2, AGESA_READ_SPD_PARAMS *info)
196 {
197         AGESA_STATUS status;
198         int spdAddress, ioBase;
199         u8 i2c_channel;
200         u8 backup;
201         device_t sm_dev;
202
203         if (info->SocketId     >= DIMENSION (spdAddressLookup      )) return AGESA_ERROR;
204         if (info->MemChannelId >= DIMENSION (spdAddressLookup[0]   )) return AGESA_ERROR;
205         if (info->DimmId       >= DIMENSION (spdAddressLookup[0][0])) return AGESA_ERROR;
206         i2c_channel = (UINT8) info->SocketId;
207
208         /* set ght i2c channel
209          * GPIO54,53 control the HC4052 S1,S0
210          *  S1 S0 true table
211          *   0  0   channel 1 (Socket1)
212          *   0  1   channel 2 (Socket2)
213          *   1  0   channel 3 (Socket3)
214          *   1  1   channel 4 (Socket4)
215          */
216         backup = sp5100_set_gpio(SP5100_GPIO53_56, i2c_channel, 0x03);
217
218         spdAddress = spdAddressLookup [info->SocketId] [info->MemChannelId] [info->DimmId];
219         if (spdAddress == 0)
220                 return AGESA_ERROR;
221
222         /*
223          * SMBus Base Address was set during southbridge early setup.
224          * e.g. sb700 IO mapped SMBUS_IO_BASE 0x6000, CIMX using 0xB00 as default
225          */
226         sm_dev = pci_locate_device(PCI_ID(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SB700_SM), 0);
227         ioBase = pci_read_config32(sm_dev, 0x90) & (0xFFFFFFF0);
228         setupFch(ioBase);
229
230         status = readspd(ioBase, spdAddress, (void *)info->Buffer, 256);
231         sp5100_restore_gpio(SP5100_GPIO53_56, backup);
232
233         return status;
234 }