blob: cf57685fc1a89e0a7016c8c1c4667550f3ca7e28 [file] [log] [blame]
Vladimir Serbinenko888d5592013-11-13 17:53:38 +01001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2005 Yinghai Lu <yinghailu@gmail.com>
5 * Copyright (C) 2009 coresystems GmbH
6 * Copyright (C) 2013 Vladimir Serbinenko
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <device/smbus_def.h>
23#include "pch.h"
24
25static void smbus_delay(void)
26{
27 inb(0x80);
28}
29
30static int smbus_wait_until_ready(u16 smbus_base)
31{
32 unsigned loops = SMBUS_TIMEOUT;
33 unsigned char byte;
34 do {
35 smbus_delay();
36 if (--loops == 0)
37 break;
38 byte = inb(smbus_base + SMBHSTSTAT);
39 } while (byte & 1);
40 return loops ? 0 : -1;
41}
42
43static int smbus_wait_until_done(u16 smbus_base)
44{
45 unsigned loops = SMBUS_TIMEOUT;
46 unsigned char byte;
47 do {
48 smbus_delay();
49 if (--loops == 0)
50 break;
51 byte = inb(smbus_base + SMBHSTSTAT);
52 } while ((byte & 1) || (byte & ~((1 << 6) | (1 << 0))) == 0);
53 return loops ? 0 : -1;
54}
55
56static int do_smbus_read_byte(unsigned smbus_base, unsigned device, unsigned address)
57{
58 unsigned char global_status_register;
59 unsigned char byte;
60
61 if (smbus_wait_until_ready(smbus_base) < 0) {
62 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
63 }
64 /* Setup transaction */
65 /* Disable interrupts */
66 outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
67 /* Set the device I'm talking too */
68 outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD);
69 /* Set the command/address... */
70 outb(address & 0xff, smbus_base + SMBHSTCMD);
71 /* Set up for a byte data read */
72 outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2),
73 (smbus_base + SMBHSTCTL));
74 /* Clear any lingering errors, so the transaction will run */
75 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
76
77 /* Clear the data byte... */
78 outb(0, smbus_base + SMBHSTDAT0);
79
80 /* Start the command */
81 outb((inb(smbus_base + SMBHSTCTL) | 0x40),
82 smbus_base + SMBHSTCTL);
83
84 /* Poll for transaction completion */
85 if (smbus_wait_until_done(smbus_base) < 0) {
86 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
87 }
88
89 global_status_register = inb(smbus_base + SMBHSTSTAT);
90
91 /* Ignore the "In Use" status... */
92 global_status_register &= ~(3 << 5);
93
94 /* Read results of transaction */
95 byte = inb(smbus_base + SMBHSTDAT0);
96 if (global_status_register != (1 << 1)) {
97 return SMBUS_ERROR;
98 }
99 return byte;
100}
101
Vladimir Serbinenko888d5592013-11-13 17:53:38 +0100102static int do_smbus_write_byte(unsigned smbus_base, unsigned device, unsigned address, unsigned data)
103{
104 unsigned char global_status_register;
105
106 if (smbus_wait_until_ready(smbus_base) < 0)
107 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
108
109 /* Setup transaction */
110 /* Disable interrupts */
111 outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
112 /* Set the device I'm talking too */
113 outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD);
114 /* Set the command/address... */
115 outb(address & 0xff, smbus_base + SMBHSTCMD);
116 /* Set up for a byte data read */
117 outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2),
118 (smbus_base + SMBHSTCTL));
119 /* Clear any lingering errors, so the transaction will run */
120 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
121
122 /* Clear the data byte... */
123 outb(data, smbus_base + SMBHSTDAT0);
124
125 /* Start the command */
126 outb((inb(smbus_base + SMBHSTCTL) | 0x40),
127 smbus_base + SMBHSTCTL);
128
129 /* Poll for transaction completion */
130 if (smbus_wait_until_done(smbus_base) < 0)
131 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
132
133 global_status_register = inb(smbus_base + SMBHSTSTAT);
134
135 /* Ignore the "In Use" status... */
136 global_status_register &= ~(3 << 5);
137
138 /* Read results of transaction */
139 if (global_status_register != (1 << 1))
140 return SMBUS_ERROR;
141
142 return 0;
143}
144
Vladimir Serbinenko5f20dbf2014-01-27 23:57:44 +0100145#ifdef __PRE_RAM__
146
Vladimir Serbinenko888d5592013-11-13 17:53:38 +0100147static int do_smbus_block_write(unsigned smbus_base, unsigned device,
148 unsigned cmd, unsigned bytes, const u8 *buf)
149{
150 u8 status;
151
152 if (smbus_wait_until_ready(smbus_base) < 0)
153 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
154
155 /* Setup transaction */
156 /* Disable interrupts */
157 outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
158 /* Set the device I'm talking too */
159 outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD);
160 /* Set the command/address... */
161 outb(cmd & 0xff, smbus_base + SMBHSTCMD);
162 /* Set up for a block data write */
163 outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x5 << 2),
164 (smbus_base + SMBHSTCTL));
165 /* Clear any lingering errors, so the transaction will run */
166 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
167
168 /* set number of bytes to transfer */
169 outb(bytes, smbus_base + SMBHSTDAT0);
170
171 outb(*buf++, smbus_base + SMBBLKDAT);
172 bytes--;
173
174 /* Start the command */
175 outb((inb(smbus_base + SMBHSTCTL) | 0x40),
176 smbus_base + SMBHSTCTL);
177
178 while(!(inb(smbus_base + SMBHSTSTAT) & 1));
179 /* Poll for transaction completion */
180 do {
181 status = inb(smbus_base + SMBHSTSTAT);
182 if (status & ((1 << 4) | /* FAILED */
183 (1 << 3) | /* BUS ERR */
184 (1 << 2))) /* DEV ERR */
185 return SMBUS_ERROR;
186
187 if (status & 0x80) { /* Byte done */
188 outb(*buf++, smbus_base + SMBBLKDAT);
189 outb(status, smbus_base + SMBHSTSTAT);
190 }
191 } while(status & 0x01);
192
193 return 0;
194}
195
196static int do_smbus_block_read(unsigned smbus_base, unsigned device,
197 unsigned cmd, unsigned bytes, u8 *buf)
198{
199 u8 status;
200 int bytes_read = 0;
201 if (smbus_wait_until_ready(smbus_base) < 0)
202 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
203
204 /* Setup transaction */
205 /* Disable interrupts */
206 outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
207 /* Set the device I'm talking too */
208 outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD);
209 /* Set the command/address... */
210 outb(cmd & 0xff, smbus_base + SMBHSTCMD);
211 /* Set up for a block data read */
212 outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x5 << 2),
213 (smbus_base + SMBHSTCTL));
214 /* Clear any lingering errors, so the transaction will run */
215 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
216
217 /* Start the command */
218 outb((inb(smbus_base + SMBHSTCTL) | 0x40),
219 smbus_base + SMBHSTCTL);
220
221 while(!(inb(smbus_base + SMBHSTSTAT) & 1));
222 /* Poll for transaction completion */
223 do {
224 status = inb(smbus_base + SMBHSTSTAT);
225 if (status & ((1 << 4) | /* FAILED */
226 (1 << 3) | /* BUS ERR */
227 (1 << 2))) /* DEV ERR */
228 return SMBUS_ERROR;
229
230 if (status & 0x80) { /* Byte done */
231 *buf = inb(smbus_base + SMBBLKDAT);
232 buf++;
233 bytes_read++;
234 outb(status, smbus_base + SMBHSTSTAT);
235 if (--bytes == 1) {
236 /* indicate that next byte is the last one */
237 outb(inb(smbus_base + SMBHSTCTL) | 0x20,
238 smbus_base + SMBHSTCTL);
239 }
240 }
241 } while(status & 0x01);
242
243 return bytes_read;
244}
245#endif