diff options
Diffstat (limited to 'src/southbridge/amd/amd8111/amd8111_smbus.h')
-rw-r--r-- | src/southbridge/amd/amd8111/amd8111_smbus.h | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/src/southbridge/amd/amd8111/amd8111_smbus.h b/src/southbridge/amd/amd8111/amd8111_smbus.h index fe9b3bff8c..6661e0279f 100644 --- a/src/southbridge/amd/amd8111/amd8111_smbus.h +++ b/src/southbridge/amd/amd8111/amd8111_smbus.h @@ -222,3 +222,106 @@ static int do_smbus_write_byte(unsigned smbus_io_base, unsigned device, unsigned return 0; } +static int do_smbus_block_read(unsigned smbus_io_base, unsigned device, unsigned cmd, u8 bytes, u8 *buf) +{ + unsigned global_status_register; + unsigned i; + u8 msglen; + + 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(cmd & 0xFF, smbus_io_base + SMBHSTCMD); + /* set up for a block data read */ + outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x5), 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 length 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 */ + msglen = inw(smbus_io_base + SMBHSTDAT) & 0x3f; + + if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) { + return SMBUS_ERROR; + } + + /* read data block */ + for(i=0; i<msglen && i<bytes; i++) { + buf[i] = inw(smbus_io_base + SMBHSTFIFO) & 0xff; + } + /* empty fifo */ + while(bytes++<msglen) { + inw(smbus_io_base + SMBHSTFIFO); + } + + return i; +} + +static int do_smbus_block_write(unsigned smbus_io_base, unsigned device, unsigned cmd, u8 bytes, const u8 *buf) +{ + unsigned global_status_register; + unsigned i; + + 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(cmd & 0xFF, smbus_io_base + SMBHSTCMD); + /* set up for a block data write */ + outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x5), 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 length word...*/ + outw(bytes, smbus_io_base + SMBHSTDAT); + + /* set the data block */ + for(i=0; i<bytes; i++) { + outw(buf[i], smbus_io_base + SMBHSTFIFO); + } + + /* 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; +} + + |