blob: 8eb36f46ee6f06315bffcce018b179483d8d0a0c [file] [log] [blame]
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +03001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 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.
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030014 */
15
16#include <device/pci_def.h>
17#include <device/device.h>
Elyes HAOUAS20eaef02019-03-29 17:45:28 +010018#include <console/console.h>
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030019/* warning: Porting.h includes an open #pragma pack(1) */
Elyes HAOUAS19f5ba82018-10-14 14:52:06 +020020#include <Porting.h>
21#include <AGESA.h>
22#include <amdlib.h>
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030023#include <northbridge/amd/agesa/dimmSpd.h>
24
25/*-----------------------------------------------------------------------------
26 *
27 * readSmbusByteData - read a single SPD byte from any offset
28 */
29
30static int readSmbusByteData (int iobase, int address, char *buffer, int offset)
31{
32 unsigned int status;
33 UINT64 limit;
34
35 address |= 1; // set read bit
36
37 __outbyte (iobase + 0, 0xFF); // clear error status
38 __outbyte (iobase + 1, 0x1F); // clear error status
39 __outbyte (iobase + 3, offset); // offset in eeprom
40 __outbyte (iobase + 4, address); // slave address and read bit
41 __outbyte (iobase + 2, 0x48); // read byte command
42
43 // time limit to avoid hanging for unexpected error status (should never happen)
44 limit = __rdtsc () + 2000000000 / 10;
45 for (;;)
46 {
47 status = __inbyte (iobase);
48 if (__rdtsc () > limit) break;
49 if ((status & 2) == 0) continue; // SMBusInterrupt not set, keep waiting
50 if ((status & 1) == 1) continue; // HostBusy set, keep waiting
51 break;
52 }
53
54 buffer [0] = __inbyte (iobase + 5);
55 if (status == 2) status = 0; // check for done with no errors
56 return status;
57}
58
59/*-----------------------------------------------------------------------------
60 *
61 * readSmbusByte - read a single SPD byte from the default offset
62 * this function is faster function readSmbusByteData
63 */
64
65static int readSmbusByte (int iobase, int address, char *buffer)
66{
67 unsigned int status;
68 UINT64 limit;
69
70 __outbyte (iobase + 0, 0xFF); // clear error status
71 __outbyte (iobase + 2, 0x44); // read command
72
73 // time limit to avoid hanging for unexpected error status
74 limit = __rdtsc () + 2000000000 / 10;
75 for (;;)
76 {
77 status = __inbyte (iobase);
78 if (__rdtsc () > limit) break;
79 if ((status & 2) == 0) continue; // SMBusInterrupt not set, keep waiting
80 if ((status & 1) == 1) continue; // HostBusy set, keep waiting
81 break;
82 }
83
84 buffer [0] = __inbyte (iobase + 5);
85 if (status == 2) status = 0; // check for done with no errors
86 return status;
87}
88
89/*---------------------------------------------------------------------------
90 *
91 * readspd - Read one or more SPD bytes from a DIMM.
92 * Start with offset zero and read sequentially.
93 * Optimization relies on autoincrement to avoid
94 * sending offset for every byte.
95 * Reads 128 bytes in 7-8 ms at 400 KHz.
96 */
97
98static int readspd (int iobase, int SmbusSlaveAddress, char *buffer, int count)
99{
100 int index, error;
101
102 printk(BIOS_SPEW, "-------------READING SPD-----------\n");
103 printk(BIOS_SPEW, "iobase: 0x%08X, SmbusSlave: 0x%08X, count: %d\n",
104 iobase, SmbusSlaveAddress, count);
105
106 /* read the first byte using offset zero */
107 error = readSmbusByteData (iobase, SmbusSlaveAddress, buffer, 0);
108
109 if (error) {
110 printk(BIOS_ERR, "-------------SPD READ ERROR-----------\n");
111 return error;
112 }
113
114 /* read the remaining bytes using auto-increment for speed */
115 for (index = 1; index < count; index++)
116 {
117 error = readSmbusByte (iobase, SmbusSlaveAddress, &buffer [index]);
118 if (error) {
119 printk(BIOS_ERR, "-------------SPD READ ERROR-----------\n");
120 return error;
121 }
122 }
123 printk(BIOS_SPEW, "\n");
124 printk(BIOS_SPEW, "-------------FINISHED READING SPD-----------\n");
125
126 return 0;
127}
128
129static void writePmReg (int reg, int data)
130{
131 __outbyte (0xCD6, reg);
132 __outbyte (0xCD7, data);
133}
134
135static void setupFch (int ioBase)
136{
137 writePmReg (0x2D, ioBase >> 8);
138 writePmReg (0x2C, ioBase | 1);
139 __outbyte (ioBase + 0x0E, 66000000 / 400000 / 4); // set SMBus clock to 400 KHz
140}
141
142int hudson_readSpd(int spdAddress, char *buf, size_t len)
143{
144 int ioBase = 0xB00;
145 setupFch (ioBase);
146 return readspd (ioBase, spdAddress, buf, len);
147}