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