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 <device/pci_def.h>
4 #include <device/device.h>
5 #include <stddef.h>
6 #include <OEM.h> /* SMBUS0_BASE_ADDRESS */
7 
8 /* warning: Porting.h includes an open #pragma pack(1) */
9 #include <vendorcode/amd/include/Porting.h>
10 #include <AGESA.h>
11 #include "chip.h"
12 #include "smbus_spd.h"
13 
15 
16 /* uncomment for source level debug - GDB gets really confused otherwise. */
17 //#pragma optimize ("", off)
18 
19 /**
20  * Read a single SPD byte. If the first byte is being read, set up the
21  * address and offset. Following bytes auto increment.
22  */
23 static UINT8 readSmbusByte(UINT16 iobase, UINT8 address, char *buffer,
24  int offset, int initial_offset)
25 {
26  unsigned int status = -1;
27  UINT64 time_limit;
28 
29  /* clear status register */
30  __outbyte(iobase + SMBUS_STATUS_REG, 0x1E);
31 
32  if (offset == initial_offset) {
33  /* Clear slave status, set offset, set slave address and start reading */
34  __outbyte(iobase + SMBUS_SLAVE_STATUS_REG, 0x3E);
35  __outbyte(iobase + SMBUS_CONTROL_REG, offset);
36  __outbyte(iobase + SMBUS_HOST_CMD_REG, address | READ_BIT);
37  __outbyte(iobase + SMBUS_COMMAND_REG, SMBUS_READ_BYTE_COMMAND);
38  } else {
39  /* Issue read command - auto increments to next byte */
40  __outbyte(iobase + SMBUS_COMMAND_REG, SMBUS_READ_COMMAND);
41  }
42  /* time limit to avoid hanging for unexpected error status */
43  time_limit = __rdtsc() + MAX_READ_TSC_COUNT;
44  while (__rdtsc() <= time_limit) {
45  status = __inbyte(iobase + SMBUS_STATUS_REG);
46  if ((status & SMBUS_INTERRUPT_MASK) == 0)
47  continue; /* SMBusInterrupt not set, keep waiting */
48  if ((status & HOSTBUSY_MASK) != 0)
49  continue; /* HostBusy set, keep waiting */
50  break;
51  }
52 
53  if (status != STATUS__COMPLETED_SUCCESSFULLY)
54  return AGESA_ERROR;
55 
56  buffer[0] = __inbyte(iobase + SMBUS_DATA0_REG);
57  return AGESA_SUCCESS;
58 }
59 
60 static void writePmReg(UINT8 reg, UINT8 data)
61 {
62  __outbyte(PMIO_INDEX_REG, reg);
63  __outbyte(PMIO_DATA_REG, data);
64 }
65 
66 static void setupFch(UINT16 ioBase)
67 {
68  /* set up SMBUS - Set to SMBUS 0 & set base address */
69  /* For SB800 & Hudson1 to SB900 & Hudson 2/3 */
70  writePmReg(SMBUS_BAR_HIGH_BYTE, ioBase >> 8);
71  writePmReg(SMBUS_BAR_LOW_BYTE, (ioBase & 0xe0) | 1);
72 
73  /* set SMBus clock to 400 KHz */
74  __outbyte(ioBase + SMBUS_CLOCK_REG, SMBUS_FREQUENCY_CONST / 400000);
75 }
76 
77 /**
78  * Read one or more SPD bytes from a DIMM.
79  * Start with offset zero and read sequentially.
80  * Reads 128 bytes in 7-8 ms at 400 KHz.
81  */
82 static UINT8 readspd(UINT16 iobase, UINT8 SmbusSlaveAddress, char *buffer,
83  UINT16 count)
84 {
85  UINT16 index;
86  UINT8 status;
87  UINT8 initial_offset = 0;
88 
89  setupFch(iobase);
90 
91  for (index = initial_offset; index < count; index++) {
92  status = readSmbusByte(iobase, SmbusSlaveAddress, &buffer[index], index,
93  initial_offset);
94  if (status != AGESA_SUCCESS)
95  return status;
96  }
97 
98  return status;
99 }
100 
101 int smbus_readSpd(int spdAddress, char *buf, size_t len)
102 {
103  int ioBase = SMBUS0_BASE_ADDRESS;
104  setupFch (ioBase);
105  return readspd (ioBase, spdAddress, buf, len);
106 }
#define AGESA_SUCCESS
Definition: Amd.h:38
#define AGESA_ERROR
Definition: Amd.h:42
static size_t offset
Definition: flashconsole.c:16
uint64_t address
Definition: fw_cfg_if.h:0
static uint8_t * buf
Definition: uart.c:7
u8 buffer[C2P_BUFFER_MAXSIZE]
Definition: psp_smm.c:18
#define MAX_READ_TSC_COUNT
Definition: smbus_spd.h:14
#define PMIO_DATA_REG
Definition: smbus_spd.h:17
#define SMBUS_READ_COMMAND
Definition: smbus_spd.h:12
#define READ_BIT
Definition: smbus_spd.h:6
#define STATUS__COMPLETED_SUCCESSFULLY
Definition: smbus_spd.h:30
#define SMBUS_HOST_CMD_REG
Definition: smbus_spd.h:26
#define SMBUS_BAR_LOW_BYTE
Definition: smbus_spd.h:19
#define SMBUS_FREQUENCY_CONST
Definition: smbus_spd.h:32
#define SMBUS_SLAVE_STATUS_REG
Definition: smbus_spd.h:23
#define SMBUS_STATUS_REG
Definition: smbus_spd.h:22
#define SMBUS_BAR_HIGH_BYTE
Definition: smbus_spd.h:20
#define SMBUS_CONTROL_REG
Definition: smbus_spd.h:25
#define SMBUS_READ_BYTE_COMMAND
Definition: smbus_spd.h:11
#define SMBUS_COMMAND_REG
Definition: smbus_spd.h:24
#define HOSTBUSY_MASK
Definition: smbus_spd.h:9
#define PMIO_INDEX_REG
Definition: smbus_spd.h:16
#define SMBUS_CLOCK_REG
Definition: smbus_spd.h:28
#define SMBUS_INTERRUPT_MASK
Definition: smbus_spd.h:8
#define SMBUS_DATA0_REG
Definition: smbus_spd.h:27
static void setupFch(UINT16 ioBase)
Definition: smbus_spd.c:66
static UINT8 readSmbusByte(UINT16 iobase, UINT8 address, char *buffer, int offset, int initial_offset)
Read a single SPD byte.
Definition: smbus_spd.c:23
int smbus_readSpd(int spdAddress, char *buf, size_t len)
Definition: smbus_spd.c:101
static UINT8 readspd(UINT16 iobase, UINT8 SmbusSlaveAddress, char *buffer, UINT16 count)
Read one or more SPD bytes from a DIMM.
Definition: smbus_spd.c:82
static void writePmReg(UINT8 reg, UINT8 data)
Definition: smbus_spd.c:60
#define count