066feade070d5976638ed20f2354dce1f28c3f6c
[coreboot.git] / src / southbridge / intel / i82801bx / i82801bx_smbus.h
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2005 Yinghai Lu <yinghailu@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
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
21 #include <device/smbus_def.h>
22
23 void enable_smbus(void);
24
25 static void smbus_delay(void)
26 {
27         inb(0x80);
28 }
29
30 static int smbus_wait_until_ready(u16 smbus_io_base)
31 {
32         unsigned loops = SMBUS_TIMEOUT;
33         unsigned char byte;
34         do {
35                 smbus_delay();
36                 if (--loops == 0)
37                         break;
38                 byte = inb(smbus_io_base + SMBHSTSTAT);
39         } while (byte & 1);
40         return loops ? 0 : -1;
41 }
42
43 static int smbus_wait_until_done(u16 smbus_io_base)
44 {
45         unsigned loops = SMBUS_TIMEOUT;
46         unsigned char byte;
47         do {
48                 smbus_delay();
49                 if (--loops == 0)
50                         break;
51                 byte = inb(smbus_io_base + SMBHSTSTAT);
52         } while ((byte & 1) || (byte & ~((1 << 6) | (1 << 0))) == 0);
53         return loops ? 0 : -1;
54 }
55
56 static int do_smbus_read_byte(u16 smbus_io_base, u8 device, u8 address)
57 {
58         unsigned char global_status_register;
59         unsigned char byte;
60
61         if (smbus_wait_until_ready(smbus_io_base) < 0) {
62                 return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
63         }
64         /* Setup transaction */
65         /* Disable interrupts */
66         outb(inb(smbus_io_base + SMBHSTCTL) & (~1), SMBUS_IO_BASE + SMBHSTCTL);
67         /* Set the device I'm talking too */
68         outb(((device & 0x7f) << 1) | 1, smbus_io_base + SMBXMITADD);
69         /* Set the command/address... */
70         outb(address & 0xff, smbus_io_base + SMBHSTCMD);
71         /* Set up for a byte data read */
72         outb((inb(smbus_io_base + SMBHSTCTL) & 0xe3) | (0x2 << 2),
73              (smbus_io_base + SMBHSTCTL));
74         /* Clear any lingering errors, so the transaction will run */
75         outb(inb(smbus_io_base + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT);
76
77         /* Clear the data byte... */
78         outb(0, smbus_io_base + SMBHSTDAT0);
79
80         /* Start the command */
81         outb((inb(smbus_io_base + SMBHSTCTL) | 0x40),
82              smbus_io_base + SMBHSTCTL);
83
84         /* Poll for transaction completion */
85         if (smbus_wait_until_done(smbus_io_base) < 0) {
86                 return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
87         }
88
89         global_status_register = inb(smbus_io_base + SMBHSTSTAT);
90
91         /* Ignore the "In Use" status... */
92         global_status_register &= ~(3 << 5);
93
94         /* Read results of transaction */
95         byte = inb(smbus_io_base + SMBHSTDAT0);
96         if (global_status_register != (1 << 1)) {
97                 return SMBUS_ERROR;
98         }
99         return byte;
100 }