blob: 90ef03ee1232d145e602fa4450ce7eb968a40c57 [file] [log] [blame]
Arthur Heymans16fe7902017-04-12 17:01:31 +02001/*
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
18#include <arch/io.h>
19#include <device/smbus_def.h>
Arthur Heymans1b04aa22017-08-04 14:28:50 +020020#include <stdlib.h>
Arthur Heymans16fe7902017-04-12 17:01:31 +020021#include "smbus.h"
22
23
24/* I801 command constants */
25#define I801_QUICK (0 << 2)
26#define I801_BYTE (1 << 2)
27#define I801_BYTE_DATA (2 << 2)
28#define I801_WORD_DATA (3 << 2)
29#define I801_BLOCK_DATA (5 << 2)
30#define I801_I2C_BLOCK_DATA (6 << 2) /* ICH5 and later */
31
32/* I801 Host Control register bits */
33#define SMBHSTCNT_INTREN (1 << 0)
34#define SMBHSTCNT_KILL (1 << 1)
35#define SMBHSTCNT_LAST_BYTE (1 << 5)
36#define SMBHSTCNT_START (1 << 6)
37#define SMBHSTCNT_PEC_EN (1 << 7) /* ICH3 and later */
38
39/* I801 Hosts Status register bits */
40#define SMBHSTSTS_BYTE_DONE (1 << 7)
41#define SMBHSTSTS_INUSE_STS (1 << 6)
42#define SMBHSTSTS_SMBALERT_STS (1 << 5)
43#define SMBHSTSTS_FAILED (1 << 4)
44#define SMBHSTSTS_BUS_ERR (1 << 3)
45#define SMBHSTSTS_DEV_ERR (1 << 2)
46#define SMBHSTSTS_INTR (1 << 1)
47#define SMBHSTSTS_HOST_BUSY (1 << 0)
48
49#define SMBUS_TIMEOUT (10 * 1000 * 100)
Kyösti Mälkkic17e8552017-08-20 23:48:23 +030050#define SMBUS_BLOCK_MAXLEN 32
Arthur Heymans16fe7902017-04-12 17:01:31 +020051
52static void smbus_delay(void)
53{
54 inb(0x80);
55}
56
57static int smbus_wait_until_ready(u16 smbus_base)
58{
59 unsigned int loops = SMBUS_TIMEOUT;
60 unsigned char byte;
61 do {
62 smbus_delay();
63 if (--loops == 0)
64 break;
65 byte = inb(smbus_base + SMBHSTSTAT);
66 } while (byte & SMBHSTSTS_HOST_BUSY);
67 return loops ? 0 : -1;
68}
69
70static int smbus_wait_until_done(u16 smbus_base)
71{
72 unsigned int loops = SMBUS_TIMEOUT;
73 unsigned char byte;
74 do {
75 smbus_delay();
76 if (--loops == 0)
77 break;
78 byte = inb(smbus_base + SMBHSTSTAT);
79 } while ((byte & SMBHSTSTS_HOST_BUSY)
80 || (byte & ~(SMBHSTSTS_INUSE_STS | SMBHSTSTS_HOST_BUSY)) == 0);
81 return loops ? 0 : -1;
82}
83
84static int smbus_wait_until_active(u16 smbus_base)
85{
86 unsigned long loops;
87 loops = SMBUS_TIMEOUT;
88 do {
89 unsigned char val;
90 smbus_delay();
91 val = inb(smbus_base + SMBHSTSTAT);
92 if ((val & SMBHSTSTS_HOST_BUSY)) {
93 break;
94 }
95 } while (--loops);
96 return loops ? 0 : -1;
97}
98
99int do_smbus_read_byte(unsigned int smbus_base, u8 device,
100 unsigned int address)
101{
102 unsigned char status;
103 unsigned char byte;
104
105 if (smbus_wait_until_ready(smbus_base) < 0)
106 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
107 /* Set up transaction */
108 /* Disable interrupts */
109 outb(inb(smbus_base + SMBHSTCTL) & ~SMBHSTCNT_INTREN,
110 smbus_base + SMBHSTCTL);
111 /* Set the device I'm talking too */
112 outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD);
113 /* Set the command/address... */
114 outb(address & 0xff, smbus_base + SMBHSTCMD);
115 /* Set up for a byte data read */
116 outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | I801_BYTE_DATA,
117 (smbus_base + SMBHSTCTL));
118 /* Clear any lingering errors, so the transaction will run */
119 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
120
121 /* Clear the data byte... */
122 outb(0, smbus_base + SMBHSTDAT0);
123
124 /* Start the command */
125 outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
126 smbus_base + SMBHSTCTL);
127
128 /* poll for it to start */
129 if (smbus_wait_until_active(smbus_base) < 0)
130 return SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT;
131
132 /* Poll for transaction completion */
133 if (smbus_wait_until_done(smbus_base) < 0)
134 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
135
136 status = inb(smbus_base + SMBHSTSTAT);
137
138 /* Ignore the "In Use" status... */
139 status &= ~(SMBHSTSTS_SMBALERT_STS | SMBHSTSTS_INUSE_STS);
140
141 /* Read results of transaction */
142 byte = inb(smbus_base + SMBHSTDAT0);
143 if (status != SMBHSTSTS_INTR)
144 return SMBUS_ERROR;
145 return byte;
146}
147
148int do_smbus_write_byte(unsigned int smbus_base, u8 device,
149 unsigned int address, unsigned int data)
150{
151 unsigned char status;
152
153 if (smbus_wait_until_ready(smbus_base) < 0)
154 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
155
156 /* Set up transaction */
157 /* Disable interrupts */
158 outb(inb(smbus_base + SMBHSTCTL) & ~SMBHSTCNT_INTREN,
159 smbus_base + SMBHSTCTL);
160 /* Set the device I'm talking too */
161 outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD);
162 /* Set the command/address... */
163 outb(address & 0xff, smbus_base + SMBHSTCMD);
164 /* Set up for a byte data read */
165 outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | I801_BYTE_DATA,
166 (smbus_base + SMBHSTCTL));
167 /* Clear any lingering errors, so the transaction will run */
168 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
169
170 /* Clear the data byte... */
171 outb(data, smbus_base + SMBHSTDAT0);
172
173 /* Start the command */
174 outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
175 smbus_base + SMBHSTCTL);
176
177 /* poll for it to start */
178 if (smbus_wait_until_active(smbus_base) < 0)
179 return SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT;
180
181 /* Poll for transaction completion */
182 if (smbus_wait_until_done(smbus_base) < 0)
183 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
184
185 status = inb(smbus_base + SMBHSTSTAT);
186
187 /* Ignore the "In Use" status... */
188 status &= ~(SMBHSTSTS_SMBALERT_STS | SMBHSTSTS_INUSE_STS);
189
190 /* Read results of transaction */
191 if (status != SMBHSTSTS_INTR)
192 return SMBUS_ERROR;
193
194 return 0;
195}
196
197int do_smbus_block_read(unsigned int smbus_base, u8 device, u8 cmd,
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200198 unsigned int max_bytes, u8 *buf)
Arthur Heymans16fe7902017-04-12 17:01:31 +0200199{
200 u8 status;
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200201 int slave_bytes;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200202 int bytes_read = 0;
203 unsigned int loops = SMBUS_TIMEOUT;
204 if (smbus_wait_until_ready(smbus_base) < 0)
205 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
206
Kyösti Mälkkic17e8552017-08-20 23:48:23 +0300207 max_bytes = MIN(SMBUS_BLOCK_MAXLEN, max_bytes);
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200208
Arthur Heymans16fe7902017-04-12 17:01:31 +0200209 /* Set up transaction */
210 /* Disable interrupts */
211 outb(inb(smbus_base + SMBHSTCTL) & ~SMBHSTCNT_INTREN,
212 smbus_base + SMBHSTCTL);
213 /* Set the device I'm talking too */
214 outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD);
215 /* Set the command/address... */
216 outb(cmd & 0xff, smbus_base + SMBHSTCMD);
217 /* Set up for a block data read */
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200218 outb((inb(smbus_base + SMBHSTCTL) & 0xc3) | I801_BLOCK_DATA,
Arthur Heymans16fe7902017-04-12 17:01:31 +0200219 (smbus_base + SMBHSTCTL));
220 /* Clear any lingering errors, so the transaction will run */
221 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
222
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200223 /* Reset number of bytes to transfer so we notice later it
224 * was really updated with the transaction. */
225 outb(0, smbus_base + SMBHSTDAT0);
226
Arthur Heymans16fe7902017-04-12 17:01:31 +0200227 /* Start the command */
228 outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
229 smbus_base + SMBHSTCTL);
230
231 /* poll for it to start */
232 if (smbus_wait_until_active(smbus_base) < 0)
233 return SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT;
234
235 /* Poll for transaction completion */
236 do {
237 loops--;
238 status = inb(smbus_base + SMBHSTSTAT);
239 if (status & (SMBHSTSTS_FAILED | /* FAILED */
240 SMBHSTSTS_BUS_ERR | /* BUS ERR */
241 SMBHSTSTS_DEV_ERR)) /* DEV ERR */
242 return SMBUS_ERROR;
243
244 if (status & SMBHSTSTS_BYTE_DONE) { /* Byte done */
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200245
246 if (bytes_read < max_bytes) {
Kyösti Mälkkic17e8552017-08-20 23:48:23 +0300247 *buf++ = inb(smbus_base + SMBBLKDAT);
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200248 bytes_read++;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200249 }
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200250
251 /* Engine internally completes the transaction
252 * and clears HOST_BUSY flag once the byte count
253 * from slave is reached.
254 */
Arthur Heymans16fe7902017-04-12 17:01:31 +0200255 outb(status, smbus_base + SMBHSTSTAT);
256 }
257 } while ((status & SMBHSTSTS_HOST_BUSY) && loops);
258
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200259 /* Post-check we received complete message. */
260 slave_bytes = inb(smbus_base + SMBHSTDAT0);
261 if (bytes_read < slave_bytes)
262 return SMBUS_ERROR;
263
Arthur Heymans16fe7902017-04-12 17:01:31 +0200264 return bytes_read;
265}
266
267int do_smbus_block_write(unsigned int smbus_base, u8 device, u8 cmd,
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200268 const unsigned int bytes, const u8 *buf)
Arthur Heymans16fe7902017-04-12 17:01:31 +0200269{
270 u8 status;
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200271 int bytes_sent = 0;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200272 unsigned int loops = SMBUS_TIMEOUT;
273
Kyösti Mälkkic17e8552017-08-20 23:48:23 +0300274 if (bytes > SMBUS_BLOCK_MAXLEN)
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200275 return SMBUS_ERROR;
276
Arthur Heymans16fe7902017-04-12 17:01:31 +0200277 if (smbus_wait_until_ready(smbus_base) < 0)
278 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
279
280 /* Set up transaction */
281 /* Disable interrupts */
282 outb(inb(smbus_base + SMBHSTCTL) & ~SMBHSTCNT_INTREN,
283 smbus_base + SMBHSTCTL);
284 /* Set the device I'm talking too */
285 outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD);
286 /* Set the command/address... */
287 outb(cmd & 0xff, smbus_base + SMBHSTCMD);
288 /* Set up for a block data write */
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200289 outb((inb(smbus_base + SMBHSTCTL) & 0xc3) | I801_BLOCK_DATA,
Arthur Heymans16fe7902017-04-12 17:01:31 +0200290 (smbus_base + SMBHSTCTL));
291 /* Clear any lingering errors, so the transaction will run */
292 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
293
294 /* set number of bytes to transfer */
295 outb(bytes, smbus_base + SMBHSTDAT0);
296
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200297 /* Send first byte from buffer, bytes_sent increments after
298 * hardware acknowledges it.
299 */
Arthur Heymans16fe7902017-04-12 17:01:31 +0200300 outb(*buf++, smbus_base + SMBBLKDAT);
Arthur Heymans16fe7902017-04-12 17:01:31 +0200301
302 /* Start the command */
303 outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
304 smbus_base + SMBHSTCTL);
305
306 /* poll for it to start */
307 if (smbus_wait_until_active(smbus_base) < 0)
308 return SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT;
309
310 /* Poll for transaction completion */
311 do {
312 loops--;
313 status = inb(smbus_base + SMBHSTSTAT);
314 if (status & (SMBHSTSTS_FAILED | /* FAILED */
315 SMBHSTSTS_BUS_ERR | /* BUS ERR */
316 SMBHSTSTS_DEV_ERR)) /* DEV ERR */
317 return SMBUS_ERROR;
318
319 if (status & SMBHSTSTS_BYTE_DONE) {
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200320 bytes_sent++;
321 if (bytes_sent < bytes)
322 outb(*buf++, smbus_base + SMBBLKDAT);
323
324 /* Engine internally completes the transaction
325 * and clears HOST_BUSY flag once the byte count
326 * has been reached.
327 */
Arthur Heymans16fe7902017-04-12 17:01:31 +0200328 outb(status, smbus_base + SMBHSTSTAT);
329 }
330 } while ((status & SMBHSTSTS_HOST_BUSY) && loops);
331
Kyösti Mälkkic17e8552017-08-20 23:48:23 +0300332 if (bytes_sent < bytes)
333 return SMBUS_ERROR;
334
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200335 return bytes_sent;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200336}
337
338/* Only since ICH5 */
339int do_i2c_block_read(unsigned int smbus_base, u8 device,
Kyösti Mälkki1e392362017-08-20 21:36:03 +0300340 unsigned int offset, const unsigned int bytes, u8 *buf)
Arthur Heymans16fe7902017-04-12 17:01:31 +0200341{
342 u8 status;
343 int bytes_read = 0;
344 unsigned int loops = SMBUS_TIMEOUT;
345 if (smbus_wait_until_ready(smbus_base) < 0)
346 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
347
348 /* Set upp transaction */
349 /* Disable interrupts */
350 outb(inb(smbus_base + SMBHSTCTL) & SMBHSTCNT_INTREN,
351 smbus_base + SMBHSTCTL);
352 /* Set the device I'm talking to */
353 outb((device & 0x7f) << 1, smbus_base + SMBXMITADD);
354
355 /* device offset */
356 outb(offset, smbus_base + SMBHSTDAT1);
357
358 /* Set up for a i2c block data read */
359 outb((inb(smbus_base + SMBHSTCTL) & 0xc3) | I801_I2C_BLOCK_DATA,
360 (smbus_base + SMBHSTCTL));
361
362 /* Clear any lingering errors, so the transaction will run */
363 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
364 /* Start the command */
365 outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
366 smbus_base + SMBHSTCTL);
367
368 /* poll for it to start */
369 if (smbus_wait_until_active(smbus_base) < 0)
370 return SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT;
371
372 /* Poll for transaction completion */
373 do {
374 loops--;
375 status = inb(smbus_base + SMBHSTSTAT);
376 if (status & (SMBHSTSTS_FAILED | /* FAILED */
377 SMBHSTSTS_BUS_ERR | /* BUS ERR */
378 SMBHSTSTS_DEV_ERR)) /* DEV ERR */
379 return SMBUS_ERROR;
380
381 if (status & SMBHSTSTS_BYTE_DONE) {
Kyösti Mälkki1e392362017-08-20 21:36:03 +0300382
383 if (bytes_read < bytes) {
384 *buf++ = inb(smbus_base + SMBBLKDAT);
385 bytes_read++;
386 }
387
388 if (bytes_read + 1 >= bytes) {
Arthur Heymans16fe7902017-04-12 17:01:31 +0200389 /* indicate that next byte is the last one */
390 outb(inb(smbus_base + SMBHSTCTL)
391 | SMBHSTCNT_LAST_BYTE,
392 smbus_base + SMBHSTCTL);
393 }
Kyösti Mälkki1e392362017-08-20 21:36:03 +0300394
Arthur Heymans16fe7902017-04-12 17:01:31 +0200395 outb(status, smbus_base + SMBHSTSTAT);
396 }
397 } while ((status & SMBHSTSTS_HOST_BUSY) && loops);
398
Kyösti Mälkki1e392362017-08-20 21:36:03 +0300399 if (bytes_read < bytes)
400 return SMBUS_ERROR;
401
Arthur Heymans16fe7902017-04-12 17:01:31 +0200402 return bytes_read;
403}