blob: fb7f8e5754fa470ae2fd88e4367617b842f75db2 [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>
Kyösti Mälkkib5d998b2017-08-20 21:36:08 +030019#include <console/console.h>
Arthur Heymans16fe7902017-04-12 17:01:31 +020020#include <device/smbus_def.h>
Arthur Heymans1b04aa22017-08-04 14:28:50 +020021#include <stdlib.h>
Arthur Heymans16fe7902017-04-12 17:01:31 +020022#include "smbus.h"
23
24
Kyösti Mälkkib5d998b2017-08-20 21:36:08 +030025#if IS_ENABLED(CONFIG_DEBUG_SMBUS)
26#define dprintk(args...) printk(BIOS_DEBUG, ##args)
27#else
28#define dprintk(args...) do {} while (0)
29#endif
30
Arthur Heymans16fe7902017-04-12 17:01:31 +020031/* I801 command constants */
32#define I801_QUICK (0 << 2)
33#define I801_BYTE (1 << 2)
34#define I801_BYTE_DATA (2 << 2)
35#define I801_WORD_DATA (3 << 2)
36#define I801_BLOCK_DATA (5 << 2)
37#define I801_I2C_BLOCK_DATA (6 << 2) /* ICH5 and later */
38
39/* I801 Host Control register bits */
40#define SMBHSTCNT_INTREN (1 << 0)
41#define SMBHSTCNT_KILL (1 << 1)
42#define SMBHSTCNT_LAST_BYTE (1 << 5)
43#define SMBHSTCNT_START (1 << 6)
44#define SMBHSTCNT_PEC_EN (1 << 7) /* ICH3 and later */
45
46/* I801 Hosts Status register bits */
47#define SMBHSTSTS_BYTE_DONE (1 << 7)
48#define SMBHSTSTS_INUSE_STS (1 << 6)
49#define SMBHSTSTS_SMBALERT_STS (1 << 5)
50#define SMBHSTSTS_FAILED (1 << 4)
51#define SMBHSTSTS_BUS_ERR (1 << 3)
52#define SMBHSTSTS_DEV_ERR (1 << 2)
53#define SMBHSTSTS_INTR (1 << 1)
54#define SMBHSTSTS_HOST_BUSY (1 << 0)
55
Kyösti Mälkki957511c2017-08-20 21:36:11 +030056/* For SMBXMITADD register. */
57#define XMIT_WRITE(dev) (((dev) << 1) | 0)
58#define XMIT_READ(dev) (((dev) << 1) | 1)
59
Arthur Heymans16fe7902017-04-12 17:01:31 +020060#define SMBUS_TIMEOUT (10 * 1000 * 100)
Elyes HAOUASb0f19882018-06-09 11:59:00 +020061#define SMBUS_BLOCK_MAXLEN 32
Arthur Heymans16fe7902017-04-12 17:01:31 +020062
63static void smbus_delay(void)
64{
65 inb(0x80);
66}
67
Kyösti Mälkki957511c2017-08-20 21:36:11 +030068static int recover_master(int smbus_base, int ret)
Arthur Heymans16fe7902017-04-12 17:01:31 +020069{
Kyösti Mälkki957511c2017-08-20 21:36:11 +030070 /* TODO: Depending of the failure, drive KILL transaction
71 * or force soft reset on SMBus master controller.
72 */
73 printk(BIOS_ERR, "SMBus: Fatal master timeout (%d)\n", ret);
74 return ret;
Arthur Heymans16fe7902017-04-12 17:01:31 +020075}
76
Kyösti Mälkki957511c2017-08-20 21:36:11 +030077static int setup_command(unsigned int smbus_base, u8 ctrl, u8 xmitadd)
Arthur Heymans16fe7902017-04-12 17:01:31 +020078{
79 unsigned int loops = SMBUS_TIMEOUT;
Kyösti Mälkki957511c2017-08-20 21:36:11 +030080 u8 host_busy;
81
Arthur Heymans16fe7902017-04-12 17:01:31 +020082 do {
83 smbus_delay();
Kyösti Mälkki957511c2017-08-20 21:36:11 +030084 host_busy = inb(smbus_base + SMBHSTSTAT) & SMBHSTSTS_HOST_BUSY;
85 } while (--loops && host_busy);
86
87 if (loops == 0)
88 return recover_master(smbus_base,
89 SMBUS_WAIT_UNTIL_READY_TIMEOUT);
90
91 /* Clear any lingering errors, so the transaction will run. */
92 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
93
94 /* Set up transaction */
95 /* Disable interrupts */
96 outb(ctrl, (smbus_base + SMBHSTCTL));
97
98 /* Set the device I'm talking to. */
99 outb(xmitadd, smbus_base + SMBXMITADD);
100
101 return 0;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200102}
103
Kyösti Mälkkia2dcf732017-08-20 21:36:15 +0300104static int execute_command(unsigned int smbus_base)
Arthur Heymans16fe7902017-04-12 17:01:31 +0200105{
Kyösti Mälkkia2dcf732017-08-20 21:36:15 +0300106 unsigned int loops = SMBUS_TIMEOUT;
107 u8 status;
108
109 /* Start the command. */
110 outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
111 smbus_base + SMBHSTCTL);
112
113 /* Poll for it to start. */
Arthur Heymans16fe7902017-04-12 17:01:31 +0200114 do {
Arthur Heymans16fe7902017-04-12 17:01:31 +0200115 smbus_delay();
Kyösti Mälkkia2dcf732017-08-20 21:36:15 +0300116
117 /* If we poll too slow, we could miss HOST_BUSY flag
118 * set and detect INTR or x_ERR flags instead here.
119 */
120 status = inb(smbus_base + SMBHSTSTAT);
121 status &= ~(SMBHSTSTS_SMBALERT_STS | SMBHSTSTS_INUSE_STS);
122 } while (--loops && status == 0);
123
124 if (loops == 0)
125 return recover_master(smbus_base,
126 SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT);
127
128 return 0;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200129}
130
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300131static int smbus_wait_until_done(u16 smbus_base)
132{
133 unsigned int loops = SMBUS_TIMEOUT;
134 unsigned char byte;
135 do {
136 smbus_delay();
137 if (--loops == 0)
138 break;
139 byte = inb(smbus_base + SMBHSTSTAT);
140 } while ((byte & SMBHSTSTS_HOST_BUSY)
141 || (byte & ~(SMBHSTSTS_INUSE_STS | SMBHSTSTS_HOST_BUSY)) == 0);
142 return loops ? 0 : -1;
143}
144
Arthur Heymans16fe7902017-04-12 17:01:31 +0200145int do_smbus_read_byte(unsigned int smbus_base, u8 device,
146 unsigned int address)
147{
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300148 int ret;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200149 unsigned char status;
150 unsigned char byte;
151
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300152 /* Set up for a byte data read. */
153 ret = setup_command(smbus_base, I801_BYTE_DATA, XMIT_READ(device));
154 if (ret < 0)
155 return ret;
156
Arthur Heymans16fe7902017-04-12 17:01:31 +0200157 /* Set the command/address... */
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300158 outb(address, smbus_base + SMBHSTCMD);
Arthur Heymans16fe7902017-04-12 17:01:31 +0200159
160 /* Clear the data byte... */
161 outb(0, smbus_base + SMBHSTDAT0);
162
163 /* Start the command */
Kyösti Mälkkia2dcf732017-08-20 21:36:15 +0300164 ret = execute_command(smbus_base);
165 if (ret < 0)
166 return ret;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200167
168 /* Poll for transaction completion */
169 if (smbus_wait_until_done(smbus_base) < 0)
170 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
171
172 status = inb(smbus_base + SMBHSTSTAT);
173
174 /* Ignore the "In Use" status... */
175 status &= ~(SMBHSTSTS_SMBALERT_STS | SMBHSTSTS_INUSE_STS);
176
177 /* Read results of transaction */
178 byte = inb(smbus_base + SMBHSTDAT0);
179 if (status != SMBHSTSTS_INTR)
180 return SMBUS_ERROR;
181 return byte;
182}
183
184int do_smbus_write_byte(unsigned int smbus_base, u8 device,
185 unsigned int address, unsigned int data)
186{
187 unsigned char status;
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300188 int ret;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200189
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300190 /* Set up for a byte data write. */
191 ret = setup_command(smbus_base, I801_BYTE_DATA, XMIT_WRITE(device));
192 if (ret < 0)
193 return ret;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200194
Arthur Heymans16fe7902017-04-12 17:01:31 +0200195 /* Set the command/address... */
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300196 outb(address, smbus_base + SMBHSTCMD);
Arthur Heymans16fe7902017-04-12 17:01:31 +0200197
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300198 /* Set the data byte... */
Arthur Heymans16fe7902017-04-12 17:01:31 +0200199 outb(data, smbus_base + SMBHSTDAT0);
200
201 /* Start the command */
Kyösti Mälkkia2dcf732017-08-20 21:36:15 +0300202 ret = execute_command(smbus_base);
203 if (ret < 0)
204 return ret;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200205
206 /* Poll for transaction completion */
207 if (smbus_wait_until_done(smbus_base) < 0)
208 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
209
210 status = inb(smbus_base + SMBHSTSTAT);
211
212 /* Ignore the "In Use" status... */
213 status &= ~(SMBHSTSTS_SMBALERT_STS | SMBHSTSTS_INUSE_STS);
214
215 /* Read results of transaction */
216 if (status != SMBHSTSTS_INTR)
217 return SMBUS_ERROR;
218
219 return 0;
220}
221
222int do_smbus_block_read(unsigned int smbus_base, u8 device, u8 cmd,
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200223 unsigned int max_bytes, u8 *buf)
Arthur Heymans16fe7902017-04-12 17:01:31 +0200224{
225 u8 status;
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300226 int ret, slave_bytes;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200227 int bytes_read = 0;
228 unsigned int loops = SMBUS_TIMEOUT;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200229
Kyösti Mälkkic17e8552017-08-20 23:48:23 +0300230 max_bytes = MIN(SMBUS_BLOCK_MAXLEN, max_bytes);
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200231
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300232 /* Set up for a block data read. */
233 ret = setup_command(smbus_base, I801_BLOCK_DATA, XMIT_READ(device));
234 if (ret < 0)
235 return ret;
236
Arthur Heymans16fe7902017-04-12 17:01:31 +0200237 /* Set the command/address... */
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300238 outb(cmd, smbus_base + SMBHSTCMD);
Arthur Heymans16fe7902017-04-12 17:01:31 +0200239
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200240 /* Reset number of bytes to transfer so we notice later it
241 * was really updated with the transaction. */
242 outb(0, smbus_base + SMBHSTDAT0);
243
Arthur Heymans16fe7902017-04-12 17:01:31 +0200244 /* Start the command */
Kyösti Mälkkia2dcf732017-08-20 21:36:15 +0300245 ret = execute_command(smbus_base);
246 if (ret < 0)
247 return ret;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200248
249 /* Poll for transaction completion */
250 do {
251 loops--;
252 status = inb(smbus_base + SMBHSTSTAT);
253 if (status & (SMBHSTSTS_FAILED | /* FAILED */
254 SMBHSTSTS_BUS_ERR | /* BUS ERR */
255 SMBHSTSTS_DEV_ERR)) /* DEV ERR */
256 return SMBUS_ERROR;
257
258 if (status & SMBHSTSTS_BYTE_DONE) { /* Byte done */
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200259
260 if (bytes_read < max_bytes) {
Kyösti Mälkkic17e8552017-08-20 23:48:23 +0300261 *buf++ = inb(smbus_base + SMBBLKDAT);
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200262 bytes_read++;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200263 }
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200264
265 /* Engine internally completes the transaction
266 * and clears HOST_BUSY flag once the byte count
267 * from slave is reached.
268 */
Arthur Heymans16fe7902017-04-12 17:01:31 +0200269 outb(status, smbus_base + SMBHSTSTAT);
270 }
271 } while ((status & SMBHSTSTS_HOST_BUSY) && loops);
272
Kyösti Mälkkif51c5fd2017-09-09 20:45:47 +0300273 /* Post-check we received complete message. */
274 slave_bytes = inb(smbus_base + SMBHSTDAT0);
275
Kyösti Mälkkib5d998b2017-08-20 21:36:08 +0300276 dprintk("%s: status = %02x, len = %d / %d, loops = %d\n",
277 __func__, status, bytes_read, slave_bytes, loops);
278
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200279 if (bytes_read < slave_bytes)
280 return SMBUS_ERROR;
281
Arthur Heymans16fe7902017-04-12 17:01:31 +0200282 return bytes_read;
283}
284
285int do_smbus_block_write(unsigned int smbus_base, u8 device, u8 cmd,
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200286 const unsigned int bytes, const u8 *buf)
Arthur Heymans16fe7902017-04-12 17:01:31 +0200287{
288 u8 status;
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300289 int ret, bytes_sent = 0;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200290 unsigned int loops = SMBUS_TIMEOUT;
291
Kyösti Mälkkic17e8552017-08-20 23:48:23 +0300292 if (bytes > SMBUS_BLOCK_MAXLEN)
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200293 return SMBUS_ERROR;
294
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300295 /* Set up for a block data write. */
296 ret = setup_command(smbus_base, I801_BLOCK_DATA, XMIT_WRITE(device));
297 if (ret < 0)
298 return ret;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200299
Arthur Heymans16fe7902017-04-12 17:01:31 +0200300 /* Set the command/address... */
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300301 outb(cmd, smbus_base + SMBHSTCMD);
Arthur Heymans16fe7902017-04-12 17:01:31 +0200302
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300303 /* Set number of bytes to transfer. */
Arthur Heymans16fe7902017-04-12 17:01:31 +0200304 outb(bytes, smbus_base + SMBHSTDAT0);
305
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200306 /* Send first byte from buffer, bytes_sent increments after
307 * hardware acknowledges it.
308 */
Arthur Heymans16fe7902017-04-12 17:01:31 +0200309 outb(*buf++, smbus_base + SMBBLKDAT);
Arthur Heymans16fe7902017-04-12 17:01:31 +0200310
311 /* Start the command */
Kyösti Mälkkia2dcf732017-08-20 21:36:15 +0300312 ret = execute_command(smbus_base);
313 if (ret < 0)
314 return ret;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200315
316 /* Poll for transaction completion */
317 do {
318 loops--;
319 status = inb(smbus_base + SMBHSTSTAT);
320 if (status & (SMBHSTSTS_FAILED | /* FAILED */
321 SMBHSTSTS_BUS_ERR | /* BUS ERR */
322 SMBHSTSTS_DEV_ERR)) /* DEV ERR */
323 return SMBUS_ERROR;
324
325 if (status & SMBHSTSTS_BYTE_DONE) {
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200326 bytes_sent++;
327 if (bytes_sent < bytes)
328 outb(*buf++, smbus_base + SMBBLKDAT);
329
330 /* Engine internally completes the transaction
331 * and clears HOST_BUSY flag once the byte count
332 * has been reached.
333 */
Arthur Heymans16fe7902017-04-12 17:01:31 +0200334 outb(status, smbus_base + SMBHSTSTAT);
335 }
336 } while ((status & SMBHSTSTS_HOST_BUSY) && loops);
337
Kyösti Mälkkib5d998b2017-08-20 21:36:08 +0300338 dprintk("%s: status = %02x, len = %d / %d, loops = %d\n",
339 __func__, status, bytes_sent, bytes, loops);
340
Kyösti Mälkkic17e8552017-08-20 23:48:23 +0300341 if (bytes_sent < bytes)
342 return SMBUS_ERROR;
343
Arthur Heymans1b04aa22017-08-04 14:28:50 +0200344 return bytes_sent;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200345}
346
347/* Only since ICH5 */
348int do_i2c_block_read(unsigned int smbus_base, u8 device,
Kyösti Mälkki1e392362017-08-20 21:36:03 +0300349 unsigned int offset, const unsigned int bytes, u8 *buf)
Arthur Heymans16fe7902017-04-12 17:01:31 +0200350{
351 u8 status;
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300352 int ret, bytes_read = 0;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200353 unsigned int loops = SMBUS_TIMEOUT;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200354
Kyösti Mälkki957511c2017-08-20 21:36:11 +0300355 /* Set up for a i2c block data read.
356 *
357 * FIXME: Address parameter changes to XMIT_READ(device) with
358 * some revision of PCH. Presumably hardware revisions that
359 * do not have i2c block write support internally set LSB.
360 */
361 ret = setup_command(smbus_base, I801_I2C_BLOCK_DATA,
362 XMIT_WRITE(device));
363 if (ret < 0)
364 return ret;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200365
366 /* device offset */
367 outb(offset, smbus_base + SMBHSTDAT1);
368
Arthur Heymans16fe7902017-04-12 17:01:31 +0200369 /* Start the command */
Kyösti Mälkkia2dcf732017-08-20 21:36:15 +0300370 ret = execute_command(smbus_base);
371 if (ret < 0)
372 return ret;
Arthur Heymans16fe7902017-04-12 17:01:31 +0200373
374 /* Poll for transaction completion */
375 do {
376 loops--;
377 status = inb(smbus_base + SMBHSTSTAT);
378 if (status & (SMBHSTSTS_FAILED | /* FAILED */
379 SMBHSTSTS_BUS_ERR | /* BUS ERR */
380 SMBHSTSTS_DEV_ERR)) /* DEV ERR */
381 return SMBUS_ERROR;
382
383 if (status & SMBHSTSTS_BYTE_DONE) {
Kyösti Mälkki1e392362017-08-20 21:36:03 +0300384
385 if (bytes_read < bytes) {
386 *buf++ = inb(smbus_base + SMBBLKDAT);
387 bytes_read++;
388 }
389
390 if (bytes_read + 1 >= bytes) {
Arthur Heymans16fe7902017-04-12 17:01:31 +0200391 /* indicate that next byte is the last one */
392 outb(inb(smbus_base + SMBHSTCTL)
393 | SMBHSTCNT_LAST_BYTE,
394 smbus_base + SMBHSTCTL);
395 }
Kyösti Mälkki1e392362017-08-20 21:36:03 +0300396
Arthur Heymans16fe7902017-04-12 17:01:31 +0200397 outb(status, smbus_base + SMBHSTSTAT);
398 }
399 } while ((status & SMBHSTSTS_HOST_BUSY) && loops);
400
Kyösti Mälkkib5d998b2017-08-20 21:36:08 +0300401 dprintk("%s: status = %02x, len = %d / %d, loops = %d\n",
402 __func__, status, bytes_read, bytes, loops);
403
Kyösti Mälkki1e392362017-08-20 21:36:03 +0300404 if (bytes_read < bytes)
405 return SMBUS_ERROR;
406
Arthur Heymans16fe7902017-04-12 17:01:31 +0200407 return bytes_read;
408}