blob: c2e57b12d37c48785f19b936a8067eec5de1821c [file] [log] [blame]
Stefan Reinauerdebb11f2008-10-29 04:46:52 +00001/*
2 * This file is part of the coreboot project.
3 *
Stefan Reinauera8e11682009-03-11 14:54:18 +00004 * Copyright (C) 2008-2009 coresystems GmbH
Stefan Reinauerdebb11f2008-10-29 04:46:52 +00005 *
Stefan Reinauera8e11682009-03-11 14:54:18 +00006 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; version 2 of
9 * the License.
Stefan Reinauerdebb11f2008-10-29 04:46:52 +000010 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
Stefan Reinauera8e11682009-03-11 14:54:18 +000021#include <console/console.h>
22#include <device/device.h>
23#include <device/path.h>
24#include <device/smbus.h>
25#include <device/pci.h>
26#include <device/pci_ids.h>
27#include <device/pci_ops.h>
Stefan Reinauerdebb11f2008-10-29 04:46:52 +000028#include <arch/io.h>
29#include "i82801gx.h"
stepan836ae292010-12-08 05:42:47 +000030#include "smbus.h"
Stefan Reinauer109ab312009-08-12 16:08:05 +000031
Stefan Reinauera8e11682009-03-11 14:54:18 +000032static int lsmbus_read_byte(device_t dev, u8 address)
Stefan Reinauerdebb11f2008-10-29 04:46:52 +000033{
34 u16 device;
35 struct resource *res;
Stefan Reinauera8e11682009-03-11 14:54:18 +000036 struct bus *pbus;
Stefan Reinauerdebb11f2008-10-29 04:46:52 +000037
Stefan Reinauer2b34db82009-02-28 20:10:20 +000038 device = dev->path.i2c.device;
Stefan Reinauera8e11682009-03-11 14:54:18 +000039 pbus = get_pbus_smbus(dev);
40 res = find_resource(pbus->dev, 0x20);
Stefan Reinauerdebb11f2008-10-29 04:46:52 +000041
42 return do_smbus_read_byte(res->base, device, address);
43}
44
Sven Schnelle718afbe2011-10-23 15:36:15 +020045static int do_smbus_write_byte(unsigned smbus_base, unsigned device, unsigned address, unsigned data)
46{
47 unsigned char global_status_register;
48
49 if (smbus_wait_until_ready(smbus_base) < 0)
50 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
51
52 /* Setup transaction */
53 /* Disable interrupts */
54 outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
55 /* Set the device I'm talking too */
56 outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD);
57 /* Set the command/address... */
58 outb(address & 0xff, smbus_base + SMBHSTCMD);
59 /* Set up for a byte data read */
60 outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2),
61 (smbus_base + SMBHSTCTL));
62 /* Clear any lingering errors, so the transaction will run */
63 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
64
65 /* Clear the data byte... */
66 outb(data, smbus_base + SMBHSTDAT0);
67
68 /* Start the command */
69 outb((inb(smbus_base + SMBHSTCTL) | 0x40),
70 smbus_base + SMBHSTCTL);
71
72 /* Poll for transaction completion */
73 if (smbus_wait_until_done(smbus_base) < 0)
74 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
75
76 global_status_register = inb(smbus_base + SMBHSTSTAT);
77
78 /* Ignore the "In Use" status... */
79 global_status_register &= ~(3 << 5);
80
81 /* Read results of transaction */
82 if (global_status_register != (1 << 1))
83 return SMBUS_ERROR;
84 return 0;
85}
86
87static int lsmbus_write_byte(device_t dev, u8 address, u8 data)
88{
89 u16 device;
90 struct resource *res;
91 struct bus *pbus;
92
93 device = dev->path.i2c.device;
94 pbus = get_pbus_smbus(dev);
95 res = find_resource(pbus->dev, 0x20);
96 return do_smbus_write_byte(res->base, device, address, data);
97}
98
99static int do_smbus_block_write(unsigned smbus_base, unsigned device,
100 unsigned cmd, unsigned bytes, const u8 *buf)
101{
102 u8 status;
103
104 if (smbus_wait_until_ready(smbus_base) < 0)
105 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
106
107 /* Setup transaction */
108 /* Disable interrupts */
109 outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
110 /* Set the device I'm talking too */
111 outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD);
112 /* Set the command/address... */
113 outb(cmd & 0xff, smbus_base + SMBHSTCMD);
114 /* Set up for a block data write */
115 outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x5 << 2),
116 (smbus_base + SMBHSTCTL));
117 /* Clear any lingering errors, so the transaction will run */
118 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
119
120 /* set number of bytes to transfer */
121 outb(bytes, smbus_base + SMBHSTDAT0);
122
123 outb(*buf++, smbus_base + SMBBLKDAT);
124 bytes--;
125
126 /* Start the command */
127 outb((inb(smbus_base + SMBHSTCTL) | 0x40),
128 smbus_base + SMBHSTCTL);
129
130 while(!(inb(smbus_base + SMBHSTSTAT) & 1));
131 /* Poll for transaction completion */
132 do {
133 status = inb(smbus_base + SMBHSTSTAT);
134 if (status & ((1 << 4) | /* FAILED */
135 (1 << 3) | /* BUS ERR */
136 (1 << 2))) /* DEV ERR */
137 return SMBUS_ERROR;
138
139 if (status & 0x80) { /* Byte done */
140 outb(*buf++, smbus_base + SMBBLKDAT);
141 outb(status, smbus_base + SMBHSTSTAT);
142 }
143 } while(status & 0x01);
144
145 return 0;
146}
147
148
149
150static int lsmbus_block_write(device_t dev, u8 cmd, u8 bytes, const u8 *buf)
151{
152 u16 device;
153 struct resource *res;
154 struct bus *pbus;
155
156 device = dev->path.i2c.device;
157 pbus = get_pbus_smbus(dev);
158 res = find_resource(pbus->dev, 0x20);
159 return do_smbus_block_write(res->base, device, cmd, bytes, buf);
160}
161
162static int do_smbus_block_read(unsigned smbus_base, unsigned device,
163 unsigned cmd, unsigned bytes, u8 *buf)
164{
165 u8 status;
166 int bytes_read = 0;
167 if (smbus_wait_until_ready(smbus_base) < 0)
168 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
169
170 /* Setup transaction */
171 /* Disable interrupts */
172 outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
173 /* Set the device I'm talking too */
174 outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD);
175 /* Set the command/address... */
176 outb(cmd & 0xff, smbus_base + SMBHSTCMD);
177 /* Set up for a block data read */
178 outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x5 << 2),
179 (smbus_base + SMBHSTCTL));
180 /* Clear any lingering errors, so the transaction will run */
181 outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
182
183 /* Start the command */
184 outb((inb(smbus_base + SMBHSTCTL) | 0x40),
185 smbus_base + SMBHSTCTL);
186
187 while(!(inb(smbus_base + SMBHSTSTAT) & 1));
188 /* Poll for transaction completion */
189 do {
190 status = inb(smbus_base + SMBHSTSTAT);
191 if (status & ((1 << 4) | /* FAILED */
192 (1 << 3) | /* BUS ERR */
193 (1 << 2))) /* DEV ERR */
194 return SMBUS_ERROR;
195
196 if (status & 0x80) { /* Byte done */
197 *buf = inb(smbus_base + SMBBLKDAT);
198 buf++;
199 bytes_read++;
200 outb(status, smbus_base + SMBHSTSTAT);
201 if (--bytes == 1) {
202 /* indicate that next byte is the last one */
203 outb(inb(smbus_base + SMBHSTCTL) | 0x20,
204 smbus_base + SMBHSTCTL);
205 }
206 }
207 } while(status & 0x01);
208
209 return bytes_read;
210}
211
212static int lsmbus_block_read(device_t dev, u8 cmd, u8 bytes, u8 *buf)
213{
214 u16 device;
215 struct resource *res;
216 struct bus *pbus;
217
218 device = dev->path.i2c.device;
219 pbus = get_pbus_smbus(dev);
220 res = find_resource(pbus->dev, 0x20);
221 return do_smbus_block_read(res->base, device, cmd, bytes, buf);
222}
223
224
Stefan Reinauerdebb11f2008-10-29 04:46:52 +0000225static struct smbus_bus_operations lops_smbus_bus = {
Stefan Reinauera8e11682009-03-11 14:54:18 +0000226 .read_byte = lsmbus_read_byte,
Sven Schnelle718afbe2011-10-23 15:36:15 +0200227 .write_byte = lsmbus_write_byte,
228 .block_read = lsmbus_block_read,
229 .block_write = lsmbus_block_write,
Stefan Reinauera8e11682009-03-11 14:54:18 +0000230};
231
232static void smbus_set_subsystem(device_t dev, unsigned vendor, unsigned device)
233{
234 if (!vendor || !device) {
235 pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
236 pci_read_config32(dev, PCI_VENDOR_ID));
237 } else {
238 pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
239 ((device & 0xffff) << 16) | (vendor & 0xffff));
240 }
241}
242
243static struct pci_operations smbus_pci_ops = {
244 .set_subsystem = smbus_set_subsystem,
Stefan Reinauerdebb11f2008-10-29 04:46:52 +0000245};
246
Sven Schnelle3c976792011-10-23 15:30:29 +0200247static void smbus_read_resources(device_t dev)
248{
249 struct resource *res = new_resource(dev, PCI_BASE_ADDRESS_4);
250 res->base = SMBUS_IO_BASE;
251 res->size = 32;
252 res->limit = res->base + res->size - 1;
253 res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_RESERVE |
254 IORESOURCE_STORED | IORESOURCE_ASSIGNED;
255}
256
Stefan Reinauerdebb11f2008-10-29 04:46:52 +0000257static struct device_operations smbus_ops = {
Sven Schnelle3c976792011-10-23 15:30:29 +0200258 .read_resources = smbus_read_resources,
Stefan Reinauerdebb11f2008-10-29 04:46:52 +0000259 .set_resources = pci_dev_set_resources,
260 .enable_resources = pci_dev_enable_resources,
Stefan Reinauerdebb11f2008-10-29 04:46:52 +0000261 .scan_bus = scan_static_bus,
262 .enable = i82801gx_enable,
263 .ops_smbus_bus = &lops_smbus_bus,
Stefan Reinauera8e11682009-03-11 14:54:18 +0000264 .ops_pci = &smbus_pci_ops,
Stefan Reinauerdebb11f2008-10-29 04:46:52 +0000265};
266
267/* 82801GB/GR/GDH/GBM/GHM/GU (ICH7/ICH7R/ICH7DH/ICH7-M/ICH7-M DH/ICH7-U) */
268static const struct pci_driver i82801gx_smbus __pci_driver = {
269 .ops = &smbus_ops,
270 .vendor = PCI_VENDOR_ID_INTEL,
Uwe Hermann5d7a1c82008-10-31 18:41:09 +0000271 .device = 0x27da,
Stefan Reinauerdebb11f2008-10-29 04:46:52 +0000272};