blob: 944c6018f12475d48f3b4b5a026dc5aadf0663e1 [file] [log] [blame]
Martin Rothebace9f2018-05-26 18:56:17 -06001/*
2 * This file is part of the coreboot project.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
Eric Biedermandbec2d42004-10-21 10:44:08 +000014#include <device/smbus_def.h>
15
16#define SMBGSTATUS 0xe0
17#define SMBGCTL 0xe2
18#define SMBHSTADDR 0xe4
19#define SMBHSTDAT 0xe6
20#define SMBHSTCMD 0xe8
21#define SMBHSTFIFO 0xe9
22
23#define SMBUS_TIMEOUT (100*1000*10)
24#define SMBUS_STATUS_MASK 0xfbff
25
26static inline void smbus_delay(void)
27{
28 outb(0x80, 0x80);
29}
30
31static int smbus_wait_until_ready(unsigned smbus_io_base)
32{
33 unsigned long loops;
34 loops = SMBUS_TIMEOUT;
35 do {
36 unsigned short val;
37 smbus_delay();
38 val = inw(smbus_io_base + SMBGSTATUS);
39 if ((val & 0x800) == 0) {
40 break;
41 }
Elyes HAOUASba28e8d2016-08-31 19:22:16 +020042 if (loops == (SMBUS_TIMEOUT / 2)) {
Stefan Reinauer14e22772010-04-27 06:56:47 +000043 outw(inw(smbus_io_base + SMBGSTATUS),
Eric Biedermandbec2d42004-10-21 10:44:08 +000044 smbus_io_base + SMBGSTATUS);
45 }
Elyes HAOUASba28e8d2016-08-31 19:22:16 +020046 } while (--loops);
Eric Biedermandbec2d42004-10-21 10:44:08 +000047 return loops?0:SMBUS_WAIT_UNTIL_READY_TIMEOUT;
48}
49
50static int smbus_wait_until_done(unsigned smbus_io_base)
51{
52 unsigned long loops;
53 loops = SMBUS_TIMEOUT;
54 do {
55 unsigned short val;
56 smbus_delay();
Stefan Reinauer14e22772010-04-27 06:56:47 +000057
Eric Biedermandbec2d42004-10-21 10:44:08 +000058 val = inw(smbus_io_base + SMBGSTATUS);
59 if (((val & 0x8) == 0) | ((val & 0x0037) != 0)) {
60 break;
61 }
Elyes HAOUASba28e8d2016-08-31 19:22:16 +020062 } while (--loops);
Eric Biedermandbec2d42004-10-21 10:44:08 +000063 return loops?0:SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
64}
65
66static int do_smbus_recv_byte(unsigned smbus_io_base, unsigned device)
67{
68 unsigned global_status_register;
69 unsigned byte;
70
71 if (smbus_wait_until_ready(smbus_io_base) < 0) {
72 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
73 }
Stefan Reinauer14e22772010-04-27 06:56:47 +000074
Eric Biedermandbec2d42004-10-21 10:44:08 +000075 /* setup transaction */
76 /* disable interrupts */
77 outw(inw(smbus_io_base + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL);
Jonathan Neuschäfer70903772017-09-23 21:39:02 +020078 /* set the device I'm talking to */
Eric Biedermandbec2d42004-10-21 10:44:08 +000079 outw(((device & 0x7f) << 1) | 1, smbus_io_base + SMBHSTADDR);
80 /* set the command/address... */
81 outb(0, smbus_io_base + SMBHSTCMD);
82 /* set up for a send byte */
83 outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x1), smbus_io_base + SMBGCTL);
84
85 /* clear any lingering errors, so the transaction will run */
86 /* Do I need to write the bits to a 1 to clear an error? */
87 outw(inw(smbus_io_base + SMBGSTATUS), smbus_io_base + SMBGSTATUS);
88
89 /* set the data word...*/
90 outw(0, smbus_io_base + SMBHSTDAT);
91
92 /* start the command */
93 outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL);
94
95
96 /* poll for transaction completion */
97 if (smbus_wait_until_done(smbus_io_base) < 0) {
98 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
99 }
100
101 global_status_register = inw(smbus_io_base + SMBGSTATUS);
102
103 /* read results of transaction */
104 byte = inw(smbus_io_base + SMBHSTDAT) & 0xff;
105
106 if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) {
107 return SMBUS_ERROR;
108 }
109 return byte;
110}
111
112static int do_smbus_send_byte(unsigned smbus_io_base, unsigned device, unsigned value)
113{
114 unsigned global_status_register;
115
116 if (smbus_wait_until_ready(smbus_io_base) < 0) {
117 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
118 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000119
Eric Biedermandbec2d42004-10-21 10:44:08 +0000120 /* setup transaction */
121 /* disable interrupts */
122 outw(inw(smbus_io_base + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL);
Jonathan Neuschäfer70903772017-09-23 21:39:02 +0200123 /* set the device I'm talking to */
Eric Biedermandbec2d42004-10-21 10:44:08 +0000124 outw(((device & 0x7f) << 1) | 0, smbus_io_base + SMBHSTADDR);
125 /* set the command/address... */
126 outb(0, smbus_io_base + SMBHSTCMD);
127 /* set up for a send byte */
128 outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x1), smbus_io_base + SMBGCTL);
129
130 /* clear any lingering errors, so the transaction will run */
131 /* Do I need to write the bits to a 1 to clear an error? */
132 outw(inw(smbus_io_base + SMBGSTATUS), smbus_io_base + SMBGSTATUS);
133
134 /* set the data word...*/
135 outw(value, smbus_io_base + SMBHSTDAT);
136
137 /* start the command */
138 outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL);
139
140
141 /* poll for transaction completion */
142 if (smbus_wait_until_done(smbus_io_base) < 0) {
143 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
144 }
145 global_status_register = inw(smbus_io_base + SMBGSTATUS);
146
147 if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) {
148 return SMBUS_ERROR;
149 }
150 return 0;
151}
152
153
154static int do_smbus_read_byte(unsigned smbus_io_base, unsigned device, unsigned address)
155{
156 unsigned global_status_register;
157 unsigned byte;
158
159 if (smbus_wait_until_ready(smbus_io_base) < 0) {
160 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
161 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000162
Eric Biedermandbec2d42004-10-21 10:44:08 +0000163 /* setup transaction */
164 /* disable interrupts */
165 outw(inw(smbus_io_base + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL);
Jonathan Neuschäfer70903772017-09-23 21:39:02 +0200166 /* set the device I'm talking to */
Eric Biedermandbec2d42004-10-21 10:44:08 +0000167 outw(((device & 0x7f) << 1) | 1, smbus_io_base + SMBHSTADDR);
168 /* set the command/address... */
169 outb(address & 0xFF, smbus_io_base + SMBHSTCMD);
170 /* set up for a byte data read */
171 outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x2), smbus_io_base + SMBGCTL);
172
173 /* clear any lingering errors, so the transaction will run */
174 /* Do I need to write the bits to a 1 to clear an error? */
175 outw(inw(smbus_io_base + SMBGSTATUS), smbus_io_base + SMBGSTATUS);
176
177 /* clear the data word...*/
178 outw(0, smbus_io_base + SMBHSTDAT);
179
180 /* start the command */
181 outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL);
182
183
184 /* poll for transaction completion */
185 if (smbus_wait_until_done(smbus_io_base) < 0) {
186 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
187 }
188
189 global_status_register = inw(smbus_io_base + SMBGSTATUS);
190
191 /* read results of transaction */
192 byte = inw(smbus_io_base + SMBHSTDAT) & 0xff;
193
194 if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) {
195 return SMBUS_ERROR;
196 }
197 return byte;
198}
199
200static int do_smbus_write_byte(unsigned smbus_io_base, unsigned device, unsigned address, unsigned char val)
201{
202 unsigned global_status_register;
203
204 if (smbus_wait_until_ready(smbus_io_base) < 0) {
205 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
206 }
207
208 /* setup transaction */
209 /* disable interrupts */
210 outw(inw(smbus_io_base + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL);
Jonathan Neuschäfer70903772017-09-23 21:39:02 +0200211 /* set the device I'm talking to */
Eric Biedermandbec2d42004-10-21 10:44:08 +0000212 outw(((device & 0x7f) << 1) | 0, smbus_io_base + SMBHSTADDR);
213 outb(address & 0xFF, smbus_io_base + SMBHSTCMD);
214 /* set up for a byte data write */ /* FIXME */
215 outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x2), smbus_io_base + SMBGCTL);
216 /* clear any lingering errors, so the transaction will run */
217 /* Do I need to write the bits to a 1 to clear an error? */
218 outw(inw(smbus_io_base + SMBGSTATUS), smbus_io_base + SMBGSTATUS);
219
220 /* write the data word...*/
221 outw(val, smbus_io_base + SMBHSTDAT);
222
223 /* start the command */
224 outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL);
225
226 /* poll for transaction completion */
227 if (smbus_wait_until_done(smbus_io_base) < 0) {
228 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
229 }
230 global_status_register = inw(smbus_io_base + SMBGSTATUS);
231
232 if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) {
233 return SMBUS_ERROR;
234 }
235 return 0;
236}
237
Oskar Enoksson9bfa1c82011-10-14 02:16:48 +0200238static int do_smbus_block_read(unsigned smbus_io_base, unsigned device, unsigned cmd, u8 bytes, u8 *buf)
239{
240 unsigned global_status_register;
241 unsigned i;
242 u8 msglen;
243
244 if (smbus_wait_until_ready(smbus_io_base) < 0) {
245 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
246 }
247
248 /* setup transaction */
249 /* disable interrupts */
250 outw(inw(smbus_io_base + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL);
Jonathan Neuschäfer70903772017-09-23 21:39:02 +0200251 /* set the device I'm talking to */
Oskar Enoksson9bfa1c82011-10-14 02:16:48 +0200252 outw(((device & 0x7f) << 1) | 1, smbus_io_base + SMBHSTADDR);
253 /* set the command/address... */
254 outb(cmd & 0xFF, smbus_io_base + SMBHSTCMD);
255 /* set up for a block data read */
256 outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x5), smbus_io_base + SMBGCTL);
257
258 /* clear any lingering errors, so the transaction will run */
259 /* Do I need to write the bits to a 1 to clear an error? */
260 outw(inw(smbus_io_base + SMBGSTATUS), smbus_io_base + SMBGSTATUS);
261
262 /* clear the length word...*/
263 outw(0, smbus_io_base + SMBHSTDAT);
264
265 /* start the command */
266 outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL);
267
268 /* poll for transaction completion */
269 if (smbus_wait_until_done(smbus_io_base) < 0) {
270 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
271 }
272
273 global_status_register = inw(smbus_io_base + SMBGSTATUS);
274
275 /* read results of transaction */
276 msglen = inw(smbus_io_base + SMBHSTDAT) & 0x3f;
277
278 if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) {
279 return SMBUS_ERROR;
280 }
281
282 /* read data block */
Elyes HAOUASc021ffe2016-09-18 19:18:56 +0200283 for (i = 0; i < msglen && i < bytes; i++) {
Oskar Enoksson9bfa1c82011-10-14 02:16:48 +0200284 buf[i] = inw(smbus_io_base + SMBHSTFIFO) & 0xff;
285 }
286 /* empty fifo */
Elyes HAOUASba28e8d2016-08-31 19:22:16 +0200287 while (bytes++<msglen) {
Oskar Enoksson9bfa1c82011-10-14 02:16:48 +0200288 inw(smbus_io_base + SMBHSTFIFO);
289 }
290
291 return i;
292}
293
294static int do_smbus_block_write(unsigned smbus_io_base, unsigned device, unsigned cmd, u8 bytes, const u8 *buf)
295{
296 unsigned global_status_register;
297 unsigned i;
298
299 if (smbus_wait_until_ready(smbus_io_base) < 0) {
300 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
301 }
302
303 /* setup transaction */
304 /* disable interrupts */
305 outw(inw(smbus_io_base + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL);
Jonathan Neuschäfer70903772017-09-23 21:39:02 +0200306 /* set the device I'm talking to */
Oskar Enoksson9bfa1c82011-10-14 02:16:48 +0200307 outw(((device & 0x7f) << 1) | 0, smbus_io_base + SMBHSTADDR);
308 /* set the command/address... */
309 outb(cmd & 0xFF, smbus_io_base + SMBHSTCMD);
310 /* set up for a block data write */
311 outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x5), smbus_io_base + SMBGCTL);
312
313 /* clear any lingering errors, so the transaction will run */
314 /* Do I need to write the bits to a 1 to clear an error? */
315 outw(inw(smbus_io_base + SMBGSTATUS), smbus_io_base + SMBGSTATUS);
316
317 /* set the length word...*/
318 outw(bytes, smbus_io_base + SMBHSTDAT);
319
320 /* set the data block */
Elyes HAOUASc021ffe2016-09-18 19:18:56 +0200321 for (i = 0; i < bytes; i++) {
Oskar Enoksson9bfa1c82011-10-14 02:16:48 +0200322 outw(buf[i], smbus_io_base + SMBHSTFIFO);
323 }
324
325 /* start the command */
326 outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL);
327
328 /* poll for transaction completion */
329 if (smbus_wait_until_done(smbus_io_base) < 0) {
330 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
331 }
332 global_status_register = inw(smbus_io_base + SMBGSTATUS);
333
334 if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) {
335 return SMBUS_ERROR;
336 }
337 return 0;
338}