blob: 14dd9ec8a780419631e0e231b5ed47554ebe849c [file] [log] [blame]
Alexandru Gagniuc88a30232013-06-04 23:37:56 -05001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me@gmail.com>
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, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <device/pci_ids.h>
21#include "early_vx900.h"
22#include <device/early_smbus.h>
23
24#include <arch/io.h>
25#include <console/console.h>
26
27/**
28 * \brief SMBUS IO ports in relation to the base IO port
29 */
30#define SMBHSTSTAT(base) (u16)(u32)base + 0x0
31#define SMBSLVSTAT(base) (u16)(u32)base + 0x1
32#define SMBHSTCTL(base) (u16)(u32)base + 0x2
33#define SMBHSTCMD(base) (u16)(u32)base + 0x3
34#define SMBXMITADD(base) (u16)(u32)base + 0x4
35#define SMBHSTDAT0(base) (u16)(u32)base + 0x5
36#define SMBHSTDAT1(base) (u16)(u32)base + 0x6
37#define SMBBLKDAT(base) (u16)(u32)base + 0x7
38#define SMBSLVCTL(base) (u16)(u32)base + 0x8
39#define SMBTRNSADD(base) (u16)(u32)base + 0x9
40#define SMBSLVDATA (base) (u16)(u32)base + 0xa
41
42static void smbus_delays(int delays)
43{
44 while (delays--)
45 smbus_delay();
46}
47
48/**
49 * Read a byte from the SMBus.
50 *
Martin Roth543888d2015-01-06 10:20:42 -070051 * @param smbus_dev The PCI address of the SMBus device .
52 * @param addr The address location of the DIMM on the SMBus.
Alexandru Gagniuc88a30232013-06-04 23:37:56 -050053 * @param offset The offset the data is located at.
54 */
55u8 smbus_read_byte(u32 smbus_dev, u8 addr, u8 offset)
56{
57 u8 val;
58
59 /* Initialize SMBUS sequence */
60 smbus_reset(smbus_dev);
61 /* Clear host data port. */
62 outb(0x00, SMBHSTDAT0(smbus_dev));
63
64 smbus_wait_until_ready(smbus_dev);
65 smbus_delays(50);
66
67 /* Actual addr to reg format. */
68 addr = (addr << 1);
69 addr |= 1; /* read command */
70 outb(addr, SMBXMITADD(smbus_dev));
71 outb(offset, SMBHSTCMD(smbus_dev));
72 /* Start transaction, byte data read. */
73 outb(0x48, SMBHSTCTL(smbus_dev));
74 smbus_wait_until_ready(smbus_dev);
75
76 val = inb(SMBHSTDAT0(smbus_dev));
77 return val;
78}
79
80void enable_smbus(void)
81{
82 device_t dev;
83 u8 reg8;
84 u32 smbus_dev = (u32) SMBUS_IO_BASE;
85
86 /* Locate the Power Management control */
87 dev = pci_locate_device(PCI_ID(PCI_VENDOR_ID_VIA,
88 PCI_DEVICE_ID_VIA_VX900_LPC), 0);
89
90 if (dev == PCI_DEV_INVALID) {
91 die("Power Management Controller not found\n");
92 }
93
94 /*
95 * To use SMBus to manage devices on the system board, it is a must to
96 * enable SMBus function by setting
97 * PMU_RXD2[0] (SMBus Controller Enable) to 1.
98 * And set PMU_RXD0 and PMU_RXD1 (SMBus I/O Base) to an appropriate
99 * I/O port address, so that all registers in SMBus I/O port can be
100 * accessed.
101 */
102
103 reg8 = pci_read_config8(dev, 0xd2);
104 /* Enable SMBus controller */
105 reg8 |= 1;
106 /* Set SMBUS clock from 128k source */
107 reg8 |= 1 << 2;
108 pci_write_config8(dev, 0xd2, reg8);
109
110 reg8 = pci_read_config8(dev, 0x94);
111 /* SMBUS clock from divider of 14.318 MHz */
112 reg8 &= ~(1 << 7);
113 pci_write_config8(dev, 0x94, reg8);
114
115 /* Set SMBus IO base */
116 pci_write_config16(dev, 0xd0, SMBUS_IO_BASE);
117
118 /*
119 * Initialize the SMBus sequence:
120 */
121 /* Clear SMBus host status register */
122 smbus_reset(smbus_dev);
123 /* Clear SMBus host data 0 register */
124 outb(0x00, SMBHSTDAT0(smbus_dev));
125
126 /* Wait for SMBUS */
127 smbus_wait_until_ready(smbus_dev);
128
129}
130
131static int spd_get_length(u8 spd_byte0)
132{
133 spd_byte0 &= 0xf;
134
135 switch (spd_byte0) {
136 case 0x3:
137 return 256;
138 case 0x2:
139 return 176;
140 case 0x1:
141 return 128;
142 default:
143 break;
144 }
145 return 0;
146}
147
148void spd_read(u8 addr, spd_raw_data spd)
149{
150 u8 reg;
151 int i, regs;
152 u32 smbus_dev = SMBUS_IO_BASE;
153
154 reg = smbus_read_byte(smbus_dev, addr, 2);
155 if (reg != 0x0b) {
156 printk(BIOS_DEBUG, "SMBUS device %x not a DDR3 module\n", addr);
157 spd[2] = 0;
158 return;
159 }
160
161 reg = smbus_read_byte(smbus_dev, addr, 0);
162 if ((regs = spd_get_length(reg)) == 0) {
163 printk(BIOS_INFO, "No DIMM present at %x\n", addr);
164 spd[2] = 0;
165 return;
166 }
167
168 for (i = 0; i < regs; i++)
169 spd[i] = smbus_read_byte(smbus_dev, addr, i);
170}
171
172void dump_spd_data(spd_raw_data spd)
173{
174 int len, i;
175 u8 reg;
176
177 if ((len = spd_get_length(spd[0])) == 0) {
178 printk(BIOS_DEBUG, "Invalid SPD\n");
179 return;
180 }
181
182 /*
183 * I originally saw this way to present SPD data in code from VIA. I
184 * really liked the idea, so here it goes.
185 */
Stefan Reinauer65b72ab2015-01-05 12:59:54 -0800186 printk(BIOS_DEBUG, " 00 01 02 03 04 05 06 07 07 09 0A 0B 0C 0D 0E 0F\n");
187 printk(BIOS_DEBUG, "---+------------------------------------------------");
Alexandru Gagniuc88a30232013-06-04 23:37:56 -0500188 for (i = 0; i < len; i++) {
189 reg = spd[i];
190 if ((i & 0x0f) == 0)
191 printk(BIOS_DEBUG, "\n%.2x |", i);
192 printk(BIOS_DEBUG, " %.2x", reg);
193 }
Stefan Reinauer65b72ab2015-01-05 12:59:54 -0800194 printk(BIOS_DEBUG, "\n");
Alexandru Gagniuc88a30232013-06-04 23:37:56 -0500195}