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