Added smbus block read/write for amd8111
authorOskar Enoksson <enok@lysator.liu.se>
Fri, 14 Oct 2011 00:16:48 +0000 (02:16 +0200)
committerStefan Reinauer <stefan.reinauer@coreboot.org>
Thu, 27 Oct 2011 17:09:50 +0000 (19:09 +0200)
Signed-off-by: Oskar Enoksson <enok@lysator.liu.se>
Change-Id: I86c80a27fd13c9a2be4034fdfb63be4ab2fadbfc
Reviewed-on: http://review.coreboot.org/281
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
src/southbridge/amd/amd8111/acpi.c
src/southbridge/amd/amd8111/amd8111_smbus.h
src/southbridge/amd/amd8111/early_smbus.c

index 2ad54b78f6717c084ca4798c60a38465b70c58ec..d5b1c1891caf676438d33ac9038cb783c62ff358 100644 (file)
@@ -66,6 +66,29 @@ static int lsmbus_write_byte(device_t dev, uint8_t address, uint8_t val)
        return do_smbus_write_byte(res->base, device, address, val);
 }
 
+static int lsmbus_block_read(device_t dev, uint8_t cmd, u8 bytes, u8 *buffer)
+{
+       unsigned device;
+       struct resource *res;
+
+       device = dev->path.i2c.device;
+       res = find_resource(get_pbus_smbus(dev)->dev, 0x58);
+
+       return do_smbus_block_read(res->base, device, cmd, bytes, buffer);
+}
+
+static int lsmbus_block_write(device_t dev, uint8_t cmd, u8 bytes, const u8 *buffer)
+{
+       unsigned device;
+       struct resource *res;
+
+       device = dev->path.i2c.device;
+       res = find_resource(get_pbus_smbus(dev)->dev, 0x58);
+
+       return do_smbus_block_write(res->base, device, cmd, bytes, buffer);
+}
+
+
 #if CONFIG_GENERATE_ACPI_TABLES == 1
 unsigned pm_base;
 #endif
@@ -191,6 +214,8 @@ static struct smbus_bus_operations lops_smbus_bus = {
        .send_byte  = lsmbus_send_byte,
        .read_byte  = lsmbus_read_byte,
        .write_byte = lsmbus_write_byte,
+       .block_read = lsmbus_block_read,
+       .block_write= lsmbus_block_write,
 };
 
 static struct pci_operations lops_pci = {
index fe9b3bff8cd3ff36ab48b7a3d85dec0d7f0b7aa7..6661e0279f0a98dc8ce114e6df354c13135c804b 100644 (file)
@@ -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;
+}
+
+
index e6d70847ea8dcf4f4717d81c4b4b6715fbcb31e6..e23628630a6f4dca47ecbe9a4413731e18850763 100644 (file)
@@ -46,3 +46,12 @@ static inline int smbus_write_byte(unsigned device, unsigned address, unsigned c
        return do_smbus_write_byte(SMBUS_IO_BASE, device, address, val);
 }
 
+static inline int smbus_block_read(unsigned device, unsigned cmd, u8 bytes, u8 *buf)
+{
+       return do_smbus_block_read(SMBUS_IO_BASE, device, cmd, bytes, buf);
+}
+
+static inline int smbus_block_write(unsigned device, unsigned cmd, u8 bytes, const u8 *buf)
+{
+       return do_smbus_block_write(SMBUS_IO_BASE, device, cmd, bytes, buf);
+}