blob: c240e5b877246fa72bade4acb5ed1a347c1ab74d [file] [log] [blame]
Angel Pons182dbde2020-04-02 23:49:05 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +03002
Michał Żygowski287ce5f2019-12-01 17:41:23 +01003#include <amdblocks/acpimmio.h>
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +03004#include <device/pci_def.h>
5#include <device/device.h>
Elyes HAOUAS20eaef02019-03-29 17:45:28 +01006#include <console/console.h>
Elyes HAOUAS24230f62020-07-27 07:56:14 +02007#include <stddef.h>
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +03008/* warning: Porting.h includes an open #pragma pack(1) */
Elyes HAOUAS19f5ba82018-10-14 14:52:06 +02009#include <Porting.h>
10#include <AGESA.h>
11#include <amdlib.h>
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030012#include <northbridge/amd/agesa/dimmSpd.h>
13
14/*-----------------------------------------------------------------------------
15 *
16 * readSmbusByteData - read a single SPD byte from any offset
17 */
18
Elyes Haouas558d7312022-07-16 09:45:39 +020019static int readSmbusByteData(int iobase, int address, char *buffer, int offset)
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030020{
21 unsigned int status;
22 UINT64 limit;
23
24 address |= 1; // set read bit
25
Elyes Haouas558d7312022-07-16 09:45:39 +020026 __outbyte(iobase + 0, 0xFF); // clear error status
27 __outbyte(iobase + 1, 0x1F); // clear error status
28 __outbyte(iobase + 3, offset); // offset in eeprom
29 __outbyte(iobase + 4, address); // slave address and read bit
30 __outbyte(iobase + 2, 0x48); // read byte command
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030031
32 // time limit to avoid hanging for unexpected error status (should never happen)
Elyes Haouas558d7312022-07-16 09:45:39 +020033 limit = __rdtsc() + 2000000000 / 10;
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030034 for (;;)
35 {
Elyes Haouas558d7312022-07-16 09:45:39 +020036 status = __inbyte(iobase);
37 if (__rdtsc() > limit) break;
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030038 if ((status & 2) == 0) continue; // SMBusInterrupt not set, keep waiting
39 if ((status & 1) == 1) continue; // HostBusy set, keep waiting
40 break;
41 }
42
Elyes Haouas558d7312022-07-16 09:45:39 +020043 buffer [0] = __inbyte(iobase + 5);
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030044 if (status == 2) status = 0; // check for done with no errors
45 return status;
46}
47
48/*-----------------------------------------------------------------------------
49 *
50 * readSmbusByte - read a single SPD byte from the default offset
51 * this function is faster function readSmbusByteData
52 */
53
Elyes Haouas558d7312022-07-16 09:45:39 +020054static int readSmbusByte(int iobase, int address, char *buffer)
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030055{
56 unsigned int status;
57 UINT64 limit;
58
Elyes Haouas558d7312022-07-16 09:45:39 +020059 __outbyte(iobase + 0, 0xFF); // clear error status
60 __outbyte(iobase + 2, 0x44); // read command
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030061
62 // time limit to avoid hanging for unexpected error status
Elyes Haouas558d7312022-07-16 09:45:39 +020063 limit = __rdtsc() + 2000000000 / 10;
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030064 for (;;)
65 {
Elyes Haouas558d7312022-07-16 09:45:39 +020066 status = __inbyte(iobase);
67 if (__rdtsc() > limit) break;
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030068 if ((status & 2) == 0) continue; // SMBusInterrupt not set, keep waiting
69 if ((status & 1) == 1) continue; // HostBusy set, keep waiting
70 break;
71 }
72
Elyes Haouas558d7312022-07-16 09:45:39 +020073 buffer [0] = __inbyte(iobase + 5);
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030074 if (status == 2) status = 0; // check for done with no errors
75 return status;
76}
77
78/*---------------------------------------------------------------------------
79 *
80 * readspd - Read one or more SPD bytes from a DIMM.
81 * Start with offset zero and read sequentially.
82 * Optimization relies on autoincrement to avoid
83 * sending offset for every byte.
84 * Reads 128 bytes in 7-8 ms at 400 KHz.
85 */
86
Elyes Haouas558d7312022-07-16 09:45:39 +020087static int readspd(int iobase, int SmbusSlaveAddress, char *buffer, int count)
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030088{
89 int index, error;
90
91 printk(BIOS_SPEW, "-------------READING SPD-----------\n");
92 printk(BIOS_SPEW, "iobase: 0x%08X, SmbusSlave: 0x%08X, count: %d\n",
93 iobase, SmbusSlaveAddress, count);
94
95 /* read the first byte using offset zero */
Elyes Haouas558d7312022-07-16 09:45:39 +020096 error = readSmbusByteData(iobase, SmbusSlaveAddress, buffer, 0);
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +030097
98 if (error) {
99 printk(BIOS_ERR, "-------------SPD READ ERROR-----------\n");
100 return error;
101 }
102
103 /* read the remaining bytes using auto-increment for speed */
104 for (index = 1; index < count; index++)
105 {
Elyes Haouas558d7312022-07-16 09:45:39 +0200106 error = readSmbusByte(iobase, SmbusSlaveAddress, &buffer [index]);
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +0300107 if (error) {
108 printk(BIOS_ERR, "-------------SPD READ ERROR-----------\n");
109 return error;
110 }
111 }
112 printk(BIOS_SPEW, "\n");
113 printk(BIOS_SPEW, "-------------FINISHED READING SPD-----------\n");
114
115 return 0;
116}
117
Elyes Haouas558d7312022-07-16 09:45:39 +0200118static void setupFch(int ioBase)
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +0300119{
Michał Żygowski287ce5f2019-12-01 17:41:23 +0100120 pm_write16(0x2c, ioBase | 1);
Elyes Haouas558d7312022-07-16 09:45:39 +0200121 __outbyte(ioBase + 0x0E, 66000000 / 400000 / 4); // set SMBus clock to 400 KHz
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +0300122}
123
124int hudson_readSpd(int spdAddress, char *buf, size_t len)
125{
126 int ioBase = 0xB00;
Elyes Haouas558d7312022-07-16 09:45:39 +0200127 setupFch(ioBase);
128 return readspd(ioBase, spdAddress, buf, len);
Kyösti Mälkkic5cc9f22014-10-17 22:33:22 +0300129}