14e2277962
while others dislike them being extra commits, let's clean them up once and for all for the existing code. If it's ugly, let it only be ugly once :-) Signed-off-by: Stefan Reinauer <stepan@coresystems.de> Acked-by: Stefan Reinauer <stepan@coresystems.de> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@5507 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
224 lines
6.3 KiB
C
224 lines
6.3 KiB
C
#include <device/smbus_def.h>
|
|
|
|
#define SMBGSTATUS 0xe0
|
|
#define SMBGCTL 0xe2
|
|
#define SMBHSTADDR 0xe4
|
|
#define SMBHSTDAT 0xe6
|
|
#define SMBHSTCMD 0xe8
|
|
#define SMBHSTFIFO 0xe9
|
|
|
|
#define SMBUS_TIMEOUT (100*1000*10)
|
|
#define SMBUS_STATUS_MASK 0xfbff
|
|
|
|
static inline void smbus_delay(void)
|
|
{
|
|
outb(0x80, 0x80);
|
|
}
|
|
|
|
static int smbus_wait_until_ready(unsigned smbus_io_base)
|
|
{
|
|
unsigned long loops;
|
|
loops = SMBUS_TIMEOUT;
|
|
do {
|
|
unsigned short val;
|
|
smbus_delay();
|
|
val = inw(smbus_io_base + SMBGSTATUS);
|
|
if ((val & 0x800) == 0) {
|
|
break;
|
|
}
|
|
if(loops == (SMBUS_TIMEOUT / 2)) {
|
|
outw(inw(smbus_io_base + SMBGSTATUS),
|
|
smbus_io_base + SMBGSTATUS);
|
|
}
|
|
} while(--loops);
|
|
return loops?0:SMBUS_WAIT_UNTIL_READY_TIMEOUT;
|
|
}
|
|
|
|
static int smbus_wait_until_done(unsigned smbus_io_base)
|
|
{
|
|
unsigned long loops;
|
|
loops = SMBUS_TIMEOUT;
|
|
do {
|
|
unsigned short val;
|
|
smbus_delay();
|
|
|
|
val = inw(smbus_io_base + SMBGSTATUS);
|
|
if (((val & 0x8) == 0) | ((val & 0x0037) != 0)) {
|
|
break;
|
|
}
|
|
} while(--loops);
|
|
return loops?0:SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
|
|
}
|
|
|
|
static int do_smbus_recv_byte(unsigned smbus_io_base, unsigned device)
|
|
{
|
|
unsigned global_status_register;
|
|
unsigned byte;
|
|
|
|
if (smbus_wait_until_ready(smbus_io_base) < 0) {
|
|
return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
|
|
}
|
|
|
|
/* setup transaction */
|
|
/* disable interrupts */
|
|
outw(inw(smbus_io_base + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL);
|
|
/* set the device I'm talking too */
|
|
outw(((device & 0x7f) << 1) | 1, smbus_io_base + SMBHSTADDR);
|
|
/* set the command/address... */
|
|
outb(0, smbus_io_base + SMBHSTCMD);
|
|
/* set up for a send byte */
|
|
outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x1), smbus_io_base + SMBGCTL);
|
|
|
|
/* clear any lingering errors, so the transaction will run */
|
|
/* Do I need to write the bits to a 1 to clear an error? */
|
|
outw(inw(smbus_io_base + SMBGSTATUS), smbus_io_base + SMBGSTATUS);
|
|
|
|
/* set the data word...*/
|
|
outw(0, smbus_io_base + SMBHSTDAT);
|
|
|
|
/* start the command */
|
|
outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL);
|
|
|
|
|
|
/* poll for transaction completion */
|
|
if (smbus_wait_until_done(smbus_io_base) < 0) {
|
|
return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
|
|
}
|
|
|
|
global_status_register = inw(smbus_io_base + SMBGSTATUS);
|
|
|
|
/* read results of transaction */
|
|
byte = inw(smbus_io_base + SMBHSTDAT) & 0xff;
|
|
|
|
if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) {
|
|
return SMBUS_ERROR;
|
|
}
|
|
return byte;
|
|
}
|
|
|
|
static int do_smbus_send_byte(unsigned smbus_io_base, unsigned device, unsigned value)
|
|
{
|
|
unsigned global_status_register;
|
|
|
|
if (smbus_wait_until_ready(smbus_io_base) < 0) {
|
|
return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
|
|
}
|
|
|
|
/* setup transaction */
|
|
/* disable interrupts */
|
|
outw(inw(smbus_io_base + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL);
|
|
/* set the device I'm talking too */
|
|
outw(((device & 0x7f) << 1) | 0, smbus_io_base + SMBHSTADDR);
|
|
/* set the command/address... */
|
|
outb(0, smbus_io_base + SMBHSTCMD);
|
|
/* set up for a send byte */
|
|
outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x1), smbus_io_base + SMBGCTL);
|
|
|
|
/* clear any lingering errors, so the transaction will run */
|
|
/* Do I need to write the bits to a 1 to clear an error? */
|
|
outw(inw(smbus_io_base + SMBGSTATUS), smbus_io_base + SMBGSTATUS);
|
|
|
|
/* set the data word...*/
|
|
outw(value, smbus_io_base + SMBHSTDAT);
|
|
|
|
/* start the command */
|
|
outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL);
|
|
|
|
|
|
/* poll for transaction completion */
|
|
if (smbus_wait_until_done(smbus_io_base) < 0) {
|
|
return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
|
|
}
|
|
global_status_register = inw(smbus_io_base + SMBGSTATUS);
|
|
|
|
if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) {
|
|
return SMBUS_ERROR;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int do_smbus_read_byte(unsigned smbus_io_base, unsigned device, unsigned address)
|
|
{
|
|
unsigned global_status_register;
|
|
unsigned byte;
|
|
|
|
if (smbus_wait_until_ready(smbus_io_base) < 0) {
|
|
return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
|
|
}
|
|
|
|
/* setup transaction */
|
|
/* disable interrupts */
|
|
outw(inw(smbus_io_base + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL);
|
|
/* set the device I'm talking too */
|
|
outw(((device & 0x7f) << 1) | 1, smbus_io_base + SMBHSTADDR);
|
|
/* set the command/address... */
|
|
outb(address & 0xFF, smbus_io_base + SMBHSTCMD);
|
|
/* set up for a byte data read */
|
|
outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x2), smbus_io_base + SMBGCTL);
|
|
|
|
/* clear any lingering errors, so the transaction will run */
|
|
/* Do I need to write the bits to a 1 to clear an error? */
|
|
outw(inw(smbus_io_base + SMBGSTATUS), smbus_io_base + SMBGSTATUS);
|
|
|
|
/* clear the data word...*/
|
|
outw(0, smbus_io_base + SMBHSTDAT);
|
|
|
|
/* start the command */
|
|
outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL);
|
|
|
|
|
|
/* poll for transaction completion */
|
|
if (smbus_wait_until_done(smbus_io_base) < 0) {
|
|
return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
|
|
}
|
|
|
|
global_status_register = inw(smbus_io_base + SMBGSTATUS);
|
|
|
|
/* read results of transaction */
|
|
byte = inw(smbus_io_base + SMBHSTDAT) & 0xff;
|
|
|
|
if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) {
|
|
return SMBUS_ERROR;
|
|
}
|
|
return byte;
|
|
}
|
|
|
|
static int do_smbus_write_byte(unsigned smbus_io_base, unsigned device, unsigned address, unsigned char val)
|
|
{
|
|
unsigned global_status_register;
|
|
|
|
if (smbus_wait_until_ready(smbus_io_base) < 0) {
|
|
return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
|
|
}
|
|
|
|
/* setup transaction */
|
|
/* disable interrupts */
|
|
outw(inw(smbus_io_base + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL);
|
|
/* set the device I'm talking too */
|
|
outw(((device & 0x7f) << 1) | 0, smbus_io_base + SMBHSTADDR);
|
|
outb(address & 0xFF, smbus_io_base + SMBHSTCMD);
|
|
/* set up for a byte data write */ /* FIXME */
|
|
outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x2), smbus_io_base + SMBGCTL);
|
|
/* clear any lingering errors, so the transaction will run */
|
|
/* Do I need to write the bits to a 1 to clear an error? */
|
|
outw(inw(smbus_io_base + SMBGSTATUS), smbus_io_base + SMBGSTATUS);
|
|
|
|
/* write the data word...*/
|
|
outw(val, smbus_io_base + SMBHSTDAT);
|
|
|
|
/* start the command */
|
|
outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL);
|
|
|
|
/* poll for transaction completion */
|
|
if (smbus_wait_until_done(smbus_io_base) < 0) {
|
|
return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
|
|
}
|
|
global_status_register = inw(smbus_io_base + SMBGSTATUS);
|
|
|
|
if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) {
|
|
return SMBUS_ERROR;
|
|
}
|
|
return 0;
|
|
}
|
|
|