blob: 26bb4fd704322980af045f9eabc1108eaaf1db91 [file] [log] [blame]
Duncan Lauriec88c54c2014-04-30 16:36:13 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2005 Yinghai Lu <yinghailu@gmail.com>
5 * Copyright (C) 2008-2009 coresystems GmbH
6 * Copyright (C) 2014 Google Inc.
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.
Duncan Lauriec88c54c2014-04-30 16:36:13 -070016 */
17
18#include <arch/io.h>
19#include <console/console.h>
20#include <device/device.h>
21#include <device/path.h>
22#include <device/smbus_def.h>
23#include <device/pci.h>
24#include <device/pci_ids.h>
Julius Werner4ee4bd52014-10-20 13:46:39 -070025#include <soc/ramstage.h>
26#include <soc/smbus.h>
Duncan Lauriec88c54c2014-04-30 16:36:13 -070027
28static void smbus_delay(void)
29{
30 inb(0x80);
31}
32
33static int smbus_wait_until_ready(u16 smbus_base)
34{
35 unsigned loops = SMBUS_TIMEOUT;
36 unsigned char byte;
37 do {
38 smbus_delay();
39 if (--loops == 0)
40 break;
41 byte = inb(smbus_base + SMBHSTSTAT);
42 } while (byte & 1);
43 return loops ? 0 : -1;
44}
45
46static int smbus_wait_until_done(u16 smbus_base)
47{
48 unsigned loops = SMBUS_TIMEOUT;
49 unsigned char byte;
50 do {
51 smbus_delay();
52 if (--loops == 0)
53 break;
54 byte = inb(smbus_base + SMBHSTSTAT);
55 } while ((byte & 1) || (byte & ~((1 << 6) | (1 << 0))) == 0);
56 return loops ? 0 : -1;
57}
58
59int do_smbus_read_byte(unsigned smbus_base, unsigned device, unsigned address)
60{
61 unsigned char global_status_register;
62 unsigned char byte;
63
64 if (smbus_wait_until_ready(smbus_base) < 0) {
65 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
66 }
67 /* Setup transaction */
68 /* Disable interrupts */
69 outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
70 /* Set the device I'm talking too */
71 outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD);
72 /* Set the command/address... */
73 outb(address & 0xff, smbus_base + SMBHSTCMD);
74 /* Set up for a byte data read */
75 outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2),
76 (smbus_base + SMBHSTCTL));
77 /* Clear any lingering errors, so the transaction will run */
78 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
79
80 /* Clear the data byte... */
81 outb(0, smbus_base + SMBHSTDAT0);
82
83 /* Start the command */
84 outb((inb(smbus_base + SMBHSTCTL) | 0x40),
85 smbus_base + SMBHSTCTL);
86
87 /* Poll for transaction completion */
88 if (smbus_wait_until_done(smbus_base) < 0) {
89 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
90 }
91
92 global_status_register = inb(smbus_base + SMBHSTSTAT);
93
94 /* Ignore the "In Use" status... */
95 global_status_register &= ~(3 << 5);
96
97 /* Read results of transaction */
98 byte = inb(smbus_base + SMBHSTDAT0);
99 if (global_status_register != (1 << 1)) {
100 return SMBUS_ERROR;
101 }
102 return byte;
103}
104
105int do_smbus_write_byte(unsigned smbus_base, unsigned device,
106 unsigned address, unsigned data)
107{
108 unsigned char global_status_register;
109
110 if (smbus_wait_until_ready(smbus_base) < 0)
111 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
112
113 /* Setup transaction */
114 /* Disable interrupts */
115 outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
116 /* Set the device I'm talking too */
117 outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD);
118 /* Set the command/address... */
119 outb(address & 0xff, smbus_base + SMBHSTCMD);
120 /* Set up for a byte data read */
121 outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2),
122 (smbus_base + SMBHSTCTL));
123 /* Clear any lingering errors, so the transaction will run */
124 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
125
126 /* Clear the data byte... */
127 outb(data, smbus_base + SMBHSTDAT0);
128
129 /* Start the command */
130 outb((inb(smbus_base + SMBHSTCTL) | 0x40),
131 smbus_base + SMBHSTCTL);
132
133 /* Poll for transaction completion */
134 if (smbus_wait_until_done(smbus_base) < 0) {
135 printk(BIOS_ERR, "SMBUS transaction timeout\n");
136 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
137 }
138
139 global_status_register = inb(smbus_base + SMBHSTSTAT);
140
141 /* Ignore the "In Use" status... */
142 global_status_register &= ~(3 << 5);
143
144 /* Read results of transaction */
145 if (global_status_register != (1 << 1)) {
146 printk(BIOS_ERR, "SMBUS transaction error\n");
147 return SMBUS_ERROR;
148 }
149
150 return 0;
151}