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