coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
smbus_spd.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <amdblocks/acpimmio.h>
4 #include <device/pci_def.h>
5 #include <device/device.h>
6 #include <console/console.h>
7 #include <stddef.h>
8 /* warning: Porting.h includes an open #pragma pack(1) */
9 #include <Porting.h>
10 #include <AGESA.h>
11 #include <amdlib.h>
13 
14 /*-----------------------------------------------------------------------------
15  *
16  * readSmbusByteData - read a single SPD byte from any offset
17  */
18 
19 static int readSmbusByteData (int iobase, int address, char *buffer, int offset)
20 {
21  unsigned int status;
22  UINT64 limit;
23 
24  address |= 1; // set read bit
25 
26  __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
31 
32  // time limit to avoid hanging for unexpected error status (should never happen)
33  limit = __rdtsc () + 2000000000 / 10;
34  for (;;)
35  {
36  status = __inbyte (iobase);
37  if (__rdtsc () > limit) break;
38  if ((status & 2) == 0) continue; // SMBusInterrupt not set, keep waiting
39  if ((status & 1) == 1) continue; // HostBusy set, keep waiting
40  break;
41  }
42 
43  buffer [0] = __inbyte (iobase + 5);
44  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 
54 static int readSmbusByte (int iobase, int address, char *buffer)
55 {
56  unsigned int status;
57  UINT64 limit;
58 
59  __outbyte (iobase + 0, 0xFF); // clear error status
60  __outbyte (iobase + 2, 0x44); // read command
61 
62  // time limit to avoid hanging for unexpected error status
63  limit = __rdtsc () + 2000000000 / 10;
64  for (;;)
65  {
66  status = __inbyte (iobase);
67  if (__rdtsc () > limit) break;
68  if ((status & 2) == 0) continue; // SMBusInterrupt not set, keep waiting
69  if ((status & 1) == 1) continue; // HostBusy set, keep waiting
70  break;
71  }
72 
73  buffer [0] = __inbyte (iobase + 5);
74  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 
87 static int readspd (int iobase, int SmbusSlaveAddress, char *buffer, int count)
88 {
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 */
96  error = readSmbusByteData (iobase, SmbusSlaveAddress, buffer, 0);
97 
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  {
106  error = readSmbusByte (iobase, SmbusSlaveAddress, &buffer [index]);
107  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 
118 static void setupFch (int ioBase)
119 {
120  pm_write16(0x2c, ioBase | 1);
121  __outbyte (ioBase + 0x0E, 66000000 / 400000 / 4); // set SMBus clock to 400 KHz
122 }
123 
124 int hudson_readSpd(int spdAddress, char *buf, size_t len)
125 {
126  int ioBase = 0xB00;
127  setupFch (ioBase);
128  return readspd (ioBase, spdAddress, buf, len);
129 }
static void pm_write16(uint8_t reg, uint16_t value)
Definition: acpimmio.h:186
#define printk(level,...)
Definition: stdlib.h:16
static size_t offset
Definition: flashconsole.c:16
uint64_t address
Definition: fw_cfg_if.h:0
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
#define BIOS_SPEW
BIOS_SPEW - Excessively verbose output.
Definition: loglevel.h:142
static uint8_t * buf
Definition: uart.c:7
u8 buffer[C2P_BUFFER_MAXSIZE]
Definition: psp_smm.c:18
static int readSmbusByte(int iobase, int address, char *buffer)
Definition: smbus_spd.c:54
static int readSmbusByteData(int iobase, int address, char *buffer, int offset)
Definition: smbus_spd.c:19
int hudson_readSpd(int spdAddress, char *buf, size_t len)
Definition: smbus_spd.c:124
static int readspd(int iobase, int SmbusSlaveAddress, char *buffer, int count)
Definition: smbus_spd.c:87
static void setupFch(int ioBase)
Definition: smbus_spd.c:118
#define count