From b8fb9043eb48ddc3dc80cf88def62ae0c7c57a69 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 20 Aug 2018 15:26:01 -0500 Subject: i2c: pm_smbus: Clean up some style issues Fix some spacing issues, remove extraneous comments, add some defines instead of hard-coding numbers. Signed-off-by: Corey Minyard Cc: Michael S. Tsirkin Cc: Paolo Bonzini Message-Id: <1534796770-10295-2-git-send-email-minyard@acm.org> Signed-off-by: Paolo Bonzini --- hw/i2c/pm_smbus.c | 58 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 20 deletions(-) (limited to 'hw/i2c') diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 0d26e0f6b5..83c2377bb4 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -22,8 +22,6 @@ #include "hw/i2c/pm_smbus.h" #include "hw/i2c/smbus.h" -/* no save/load? */ - #define SMBHSTSTS 0x00 #define SMBHSTCNT 0x02 #define SMBHSTCMD 0x03 @@ -32,19 +30,34 @@ #define SMBHSTDAT1 0x06 #define SMBBLKDAT 0x07 -#define STS_HOST_BUSY (1) -#define STS_INTR (1<<1) -#define STS_DEV_ERR (1<<2) -#define STS_BUS_ERR (1<<3) -#define STS_FAILED (1<<4) -#define STS_SMBALERT (1<<5) -#define STS_INUSE_STS (1<<6) -#define STS_BYTE_DONE (1<<7) +#define STS_HOST_BUSY (1 << 0) +#define STS_INTR (1 << 1) +#define STS_DEV_ERR (1 << 2) +#define STS_BUS_ERR (1 << 3) +#define STS_FAILED (1 << 4) +#define STS_SMBALERT (1 << 5) +#define STS_INUSE_STS (1 << 6) +#define STS_BYTE_DONE (1 << 7) /* Signs of successfully transaction end : * ByteDoneStatus = 1 (STS_BYTE_DONE) and INTR = 1 (STS_INTR ) */ -//#define DEBUG +#define CTL_INTREN (1 << 0) +#define CTL_KILL (1 << 1) +#define CTL_LAST_BYTE (1 << 5) +#define CTL_START (1 << 6) +#define CTL_PEC_EN (1 << 7) +#define CTL_RETURN_MASK 0x1f + +#define PROT_QUICK 0 +#define PROT_BYTE 1 +#define PROT_BYTE_DATA 2 +#define PROT_WORD_DATA 3 +#define PROT_PROC_CALL 4 +#define PROT_BLOCK_DATA 5 +#define PROT_I2C_BLOCK_DATA 6 + +/*#define DEBUG*/ #ifdef DEBUG # define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) @@ -70,11 +83,12 @@ static void smb_transaction(PMSMBus *s) if ((s->smb_stat & STS_DEV_ERR) != 0) { goto error; } + switch(prot) { - case 0x0: + case PROT_QUICK: ret = smbus_quick_command(bus, addr, read); goto done; - case 0x1: + case PROT_BYTE: if (read) { ret = smbus_receive_byte(bus, addr); goto data8; @@ -82,7 +96,7 @@ static void smb_transaction(PMSMBus *s) ret = smbus_send_byte(bus, addr, cmd); goto done; } - case 0x2: + case PROT_BYTE_DATA: if (read) { ret = smbus_read_byte(bus, addr, cmd); goto data8; @@ -91,16 +105,17 @@ static void smb_transaction(PMSMBus *s) goto done; } break; - case 0x3: + case PROT_WORD_DATA: if (read) { ret = smbus_read_word(bus, addr, cmd); goto data16; } else { - ret = smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); + ret = smbus_write_word(bus, addr, cmd, + (s->smb_data1 << 8) | s->smb_data0); goto done; } break; - case 0x5: + case PROT_I2C_BLOCK_DATA: if (read) { ret = smbus_read_block(bus, addr, cmd, s->smb_data); goto data8; @@ -158,8 +173,9 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, break; case SMBHSTCNT: s->smb_ctl = val; - if (val & 0x40) + if (s->smb_ctl & CTL_START) { smb_transaction_start(s); + } break; case SMBHSTCMD: s->smb_cmd = val; @@ -198,7 +214,7 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) break; case SMBHSTCNT: s->smb_index = 0; - val = s->smb_ctl & 0x1f; + val = s->smb_ctl & CTL_RETURN_MASK; break; case SMBHSTCMD: val = s->smb_cmd; @@ -221,7 +237,9 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) val = 0; break; } - SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", addr, val); + SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", + addr, val); + return val; } -- cgit v1.2.3-55-g7522 From 4b615be540d8566f4b981245b4d5401163c57ced Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 20 Aug 2018 15:26:02 -0500 Subject: i2c: pm_smbus: Fix the semantics of block I2C transfers The I2C block transfer commands was not implemented correctly, it read a length byte and such like it was an smbus transfer. So fix the smbus_read_block() and smbus_write_block() functions so they can properly handle I2C transfers, and normal SMBus transfers (for upcoming changes). Pass in a transfer size and a bool to know whether to use the size byte (like SMBus) or use the length given (like I2C). Signed-off-by: Corey Minyard Cc: Michael S. Tsirkin Cc: Paolo Bonzini Message-Id: <1534796770-10295-3-git-send-email-minyard@acm.org> Signed-off-by: Paolo Bonzini --- hw/i2c/pm_smbus.c | 10 ++++++++-- hw/i2c/smbus.c | 37 ++++++++++++++++++++++++------------- include/hw/i2c/smbus.h | 17 +++++++++++++++-- 3 files changed, 47 insertions(+), 17 deletions(-) (limited to 'hw/i2c') diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 83c2377bb4..f1fe889043 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -117,10 +117,16 @@ static void smb_transaction(PMSMBus *s) break; case PROT_I2C_BLOCK_DATA: if (read) { - ret = smbus_read_block(bus, addr, cmd, s->smb_data); + int xfersize = s->smb_data0; + if (xfersize > sizeof(s->smb_data)) { + xfersize = sizeof(s->smb_data); + } + ret = smbus_read_block(bus, addr, s->smb_data1, s->smb_data, + xfersize, false, true); goto data8; } else { - ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); + ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0, + false); goto done; } break; diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c index 587ce1ab7f..6ff77c582f 100644 --- a/hw/i2c/smbus.c +++ b/hw/i2c/smbus.c @@ -293,33 +293,42 @@ int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data) return 0; } -int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data) +int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool recv_len, bool send_cmd) { - int len; + int rlen; int i; - if (i2c_start_transfer(bus, addr, 0)) { - return -1; + if (send_cmd) { + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); } - i2c_send(bus, command); if (i2c_start_transfer(bus, addr, 1)) { - i2c_end_transfer(bus); + if (send_cmd) { + i2c_end_transfer(bus); + } return -1; } - len = i2c_recv(bus); - if (len > 32) { - len = 0; + if (recv_len) { + rlen = i2c_recv(bus); + } else { + rlen = len; } - for (i = 0; i < len; i++) { + if (rlen > len) { + rlen = 0; + } + for (i = 0; i < rlen; i++) { data[i] = i2c_recv(bus); } i2c_nack(bus); i2c_end_transfer(bus); - return len; + return rlen; } int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, - int len) + int len, bool send_len) { int i; @@ -330,7 +339,9 @@ int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, return -1; } i2c_send(bus, command); - i2c_send(bus, len); + if (send_len) { + i2c_send(bus, len); + } for (i = 0; i < len; i++) { i2c_send(bus, data[i]); } diff --git a/include/hw/i2c/smbus.h b/include/hw/i2c/smbus.h index 4fdba022c1..d8b1b9ee81 100644 --- a/include/hw/i2c/smbus.h +++ b/include/hw/i2c/smbus.h @@ -72,9 +72,22 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command); int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data); int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command); int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data); -int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data); + +/* + * Do a block transfer from an I2C device. If recv_len is set, then the + * first received byte is a length field and is used to know how much data + * to receive. Otherwise receive "len" bytes. If send_cmd is set, send + * the command byte first before receiving the data. + */ +int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool recv_len, bool send_cmd); + +/* + * Do a block transfer to an I2C device. If send_len is set, send the + * "len" value before the data. + */ int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, - int len); + int len, bool send_len); void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf); void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, -- cgit v1.2.3-55-g7522 From 00bdfeab1584e68bad76034e4ffc33595533fe7d Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 20 Aug 2018 15:26:03 -0500 Subject: i2c: pm_smbus: Make the I2C block read command read-only It did have write capability, but the manual says the behavior with write enabled is undefined. So just set an error in this case. Signed-off-by: Corey Minyard Cc: Michael S. Tsirkin Cc: Paolo Bonzini Message-Id: <1534796770-10295-4-git-send-email-minyard@acm.org> Signed-off-by: Paolo Bonzini --- hw/i2c/pm_smbus.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'hw/i2c') diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index f1fe889043..dc61f2c20b 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -55,7 +55,7 @@ #define PROT_WORD_DATA 3 #define PROT_PROC_CALL 4 #define PROT_BLOCK_DATA 5 -#define PROT_I2C_BLOCK_DATA 6 +#define PROT_I2C_BLOCK_READ 6 /*#define DEBUG*/ @@ -115,7 +115,7 @@ static void smb_transaction(PMSMBus *s) goto done; } break; - case PROT_I2C_BLOCK_DATA: + case PROT_I2C_BLOCK_READ: if (read) { int xfersize = s->smb_data0; if (xfersize > sizeof(s->smb_data)) { @@ -125,9 +125,8 @@ static void smb_transaction(PMSMBus *s) xfersize, false, true); goto data8; } else { - ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0, - false); - goto done; + /* The manual says the behavior is undefined, just set DEV_ERR. */ + goto error; } break; default: -- cgit v1.2.3-55-g7522 From 38ad4fae43b9c57a4ef3111217b110b25dbd3c50 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 20 Aug 2018 15:26:04 -0500 Subject: i2c: pm_smbus: Add block transfer capability There was no block transfer code in pm_smbus.c, and it is needed for some devices. So add it. This adds both byte-by-byte block transfers and buffered block transfers. Signed-off-by: Corey Minyard Cc: Michael S. Tsirkin Cc: Paolo Bonzini Message-Id: <1534796770-10295-5-git-send-email-minyard@acm.org> Signed-off-by: Paolo Bonzini --- hw/i2c/pm_smbus.c | 151 ++++++++++++++++++++++++++++++++++++++++++---- hw/i2c/smbus_ich9.c | 8 ++- include/hw/i2c/pm_smbus.h | 20 +++++- 3 files changed, 164 insertions(+), 15 deletions(-) (limited to 'hw/i2c') diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index dc61f2c20b..32132bee3c 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -29,6 +29,7 @@ #define SMBHSTDAT0 0x05 #define SMBHSTDAT1 0x06 #define SMBBLKDAT 0x07 +#define SMBAUXCTL 0x0d #define STS_HOST_BUSY (1 << 0) #define STS_INTR (1 << 1) @@ -57,6 +58,10 @@ #define PROT_BLOCK_DATA 5 #define PROT_I2C_BLOCK_READ 6 +#define AUX_PEC (1 << 0) +#define AUX_BLK (1 << 1) +#define AUX_MASK 0x3 + /*#define DEBUG*/ #ifdef DEBUG @@ -129,6 +134,51 @@ static void smb_transaction(PMSMBus *s) goto error; } break; + case PROT_BLOCK_DATA: + if (read) { + ret = smbus_read_block(bus, addr, cmd, s->smb_data, + sizeof(s->smb_data), !s->i2c_enable, + !s->i2c_enable); + if (ret < 0) { + goto error; + } + s->smb_index = 0; + s->op_done = false; + if (s->smb_auxctl & AUX_BLK) { + s->smb_stat |= STS_INTR; + } else { + s->smb_blkdata = s->smb_data[0]; + s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; + } + s->smb_data0 = ret; + goto out; + } else { + if (s->smb_auxctl & AUX_BLK) { + if (s->smb_index != s->smb_data0) { + s->smb_index = 0; + goto error; + } + /* Data is already all written to the queue, just do + the operation. */ + s->smb_index = 0; + ret = smbus_write_block(bus, addr, cmd, s->smb_data, + s->smb_data0, !s->i2c_enable); + if (ret < 0) { + goto error; + } + s->op_done = true; + s->smb_stat |= STS_INTR; + s->smb_stat &= ~STS_HOST_BUSY; + } else { + s->op_done = false; + s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; + s->smb_data[0] = s->smb_blkdata; + s->smb_index = 0; + ret = 0; + } + goto out; + } + break; default: goto error; } @@ -148,13 +198,13 @@ done: if (ret < 0) { goto error; } - s->smb_stat |= STS_BYTE_DONE | STS_INTR; + s->smb_stat |= STS_INTR; +out: return; error: s->smb_stat |= STS_DEV_ERR; return; - } static void smb_transaction_start(PMSMBus *s) @@ -173,14 +223,61 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, " val=0x%02" PRIx64 "\n", addr, val); switch(addr) { case SMBHSTSTS: - s->smb_stat = (~(val & 0xff)) & s->smb_stat; - s->smb_index = 0; + s->smb_stat &= ~(val & ~STS_HOST_BUSY); + if (!s->op_done && !(s->smb_auxctl & AUX_BLK)) { + uint8_t read = s->smb_addr & 0x01; + + s->smb_index++; + if (!read && s->smb_index == s->smb_data0) { + uint8_t prot = (s->smb_ctl >> 2) & 0x07; + uint8_t cmd = s->smb_cmd; + uint8_t addr = s->smb_addr >> 1; + int ret; + + if (prot == PROT_I2C_BLOCK_READ) { + s->smb_stat |= STS_DEV_ERR; + goto out; + } + + ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data, + s->smb_data0, !s->i2c_enable); + if (ret < 0) { + s->smb_stat |= STS_DEV_ERR; + goto out; + } + s->op_done = true; + s->smb_stat |= STS_INTR; + s->smb_stat &= ~STS_HOST_BUSY; + } else if (!read) { + s->smb_data[s->smb_index] = s->smb_blkdata; + s->smb_stat |= STS_BYTE_DONE; + } else if (s->smb_ctl & CTL_LAST_BYTE) { + s->op_done = true; + s->smb_blkdata = s->smb_data[s->smb_index]; + s->smb_index = 0; + s->smb_stat |= STS_INTR; + s->smb_stat &= ~STS_HOST_BUSY; + } else { + s->smb_blkdata = s->smb_data[s->smb_index]; + s->smb_stat |= STS_BYTE_DONE; + } + } break; case SMBHSTCNT: - s->smb_ctl = val; - if (s->smb_ctl & CTL_START) { + s->smb_ctl = val & ~CTL_START; /* CTL_START always reads 0 */ + if (val & CTL_START) { + if (!s->op_done) { + s->smb_index = 0; + s->op_done = true; + } smb_transaction_start(s); } + if (s->smb_ctl & CTL_KILL) { + s->op_done = true; + s->smb_index = 0; + s->smb_stat |= STS_FAILED; + s->smb_stat &= ~STS_HOST_BUSY; + } break; case SMBHSTCMD: s->smb_cmd = val; @@ -195,13 +292,24 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, s->smb_data1 = val; break; case SMBBLKDAT: - s->smb_data[s->smb_index++] = val; - if (s->smb_index > 31) + if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { s->smb_index = 0; + } + if (s->smb_auxctl & AUX_BLK) { + s->smb_data[s->smb_index++] = val; + } else { + s->smb_blkdata = val; + } + break; + case SMBAUXCTL: + s->smb_auxctl = val & AUX_MASK; break; default: break; } + + out: + return; } static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) @@ -218,7 +326,6 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) } break; case SMBHSTCNT: - s->smb_index = 0; val = s->smb_ctl & CTL_RETURN_MASK; break; case SMBHSTCMD: @@ -234,9 +341,22 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) val = s->smb_data1; break; case SMBBLKDAT: - val = s->smb_data[s->smb_index++]; - if (s->smb_index > 31) + if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { s->smb_index = 0; + } + if (s->smb_auxctl & AUX_BLK) { + val = s->smb_data[s->smb_index++]; + if (!s->op_done && s->smb_index == s->smb_data0) { + s->op_done = true; + s->smb_index = 0; + s->smb_stat &= ~STS_HOST_BUSY; + } + } else { + val = s->smb_blkdata; + } + break; + case SMBAUXCTL: + val = s->smb_auxctl; break; default: val = 0; @@ -248,6 +368,13 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) return val; } +static void pm_smbus_reset(PMSMBus *s) +{ + s->op_done = true; + s->smb_index = 0; + s->smb_stat = 0; +} + static const MemoryRegionOps pm_smbus_ops = { .read = smb_ioport_readb, .write = smb_ioport_writeb, @@ -258,6 +385,8 @@ static const MemoryRegionOps pm_smbus_ops = { void pm_smbus_init(DeviceState *parent, PMSMBus *smb) { + smb->op_done = true; + smb->reset = pm_smbus_reset; smb->smbus = i2c_init_bus(parent, "i2c"); memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb, "pm-smbus", 64); diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 007cb6701d..a66a1144c5 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -61,12 +61,16 @@ static void ich9_smbus_write_config(PCIDevice *d, uint32_t address, pci_default_write_config(d, address, val, len); if (range_covers_byte(address, len, ICH9_SMB_HOSTC)) { uint8_t hostc = s->dev.config[ICH9_SMB_HOSTC]; - if ((hostc & ICH9_SMB_HOSTC_HST_EN) && - !(hostc & ICH9_SMB_HOSTC_I2C_EN)) { + if (hostc & ICH9_SMB_HOSTC_HST_EN) { memory_region_set_enabled(&s->smb.io, true); } else { memory_region_set_enabled(&s->smb.io, false); } + s->smb.i2c_enable = (hostc & ICH9_SMB_HOSTC_I2C_EN) != 0; + if (hostc & ICH9_SMB_HOSTC_SSRESET) { + s->smb.reset(&s->smb); + s->dev.config[ICH9_SMB_HOSTC] &= ~ICH9_SMB_HOSTC_SSRESET; + } } } diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h index 2a837afdcb..99d5489e1b 100644 --- a/include/hw/i2c/pm_smbus.h +++ b/include/hw/i2c/pm_smbus.h @@ -1,6 +1,8 @@ #ifndef PM_SMBUS_H #define PM_SMBUS_H +#define PM_SMBUS_MAX_MSG_SIZE 32 + typedef struct PMSMBus { I2CBus *smbus; MemoryRegion io; @@ -11,8 +13,22 @@ typedef struct PMSMBus { uint8_t smb_addr; uint8_t smb_data0; uint8_t smb_data1; - uint8_t smb_data[32]; - uint8_t smb_index; + uint8_t smb_data[PM_SMBUS_MAX_MSG_SIZE]; + uint8_t smb_blkdata; + uint8_t smb_auxctl; + uint32_t smb_index; + + /* Set by pm_smbus.c */ + void (*reset)(struct PMSMBus *s); + + /* Set by the user. */ + bool i2c_enable; + + /* Internally used by pm_smbus. */ + + /* Set on block transfers after the last byte has been read, so the + INTR bit can be set at the right time. */ + bool op_done; } PMSMBus; void pm_smbus_init(DeviceState *parent, PMSMBus *smb); -- cgit v1.2.3-55-g7522 From e724385a7071483b95c3cc8a33d130f781daa217 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 20 Aug 2018 15:26:06 -0500 Subject: i2c: pm_smbus: Add interrupt handling Add the necessary code so that interrupts actually work from the pm_smbus device. Signed-off-by: Corey Minyard Cc: Michael S. Tsirkin Cc: Paolo Bonzini Message-Id: <1534796770-10295-7-git-send-email-minyard@acm.org> Signed-off-by: Paolo Bonzini --- hw/i2c/pm_smbus.c | 14 +++++++++++++- hw/i2c/smbus_ich9.c | 16 ++++++++++++++++ include/hw/i2c/pm_smbus.h | 2 ++ 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'hw/i2c') diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 32132bee3c..6322f070be 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -214,6 +214,12 @@ static void smb_transaction_start(PMSMBus *s) s->smb_stat |= STS_HOST_BUSY; } +static bool +smb_irq_value(PMSMBus *s) +{ + return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN); +} + static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, unsigned width) { @@ -309,7 +315,9 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, } out: - return; + if (s->set_irq) { + s->set_irq(s, smb_irq_value(s)); + } } static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) @@ -365,6 +373,10 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", addr, val); + if (s->set_irq) { + s->set_irq(s, smb_irq_value(s)); + } + return val; } diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index a66a1144c5..522a703c26 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -40,6 +40,8 @@ typedef struct ICH9SMBState { PCIDevice dev; + bool irq_enabled; + PMSMBus smb; } ICH9SMBState; @@ -109,11 +111,25 @@ static void ich9_smb_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } +static void ich9_smb_set_irq(PMSMBus *pmsmb, bool enabled) +{ + ICH9SMBState *s = pmsmb->opaque; + + if (enabled == s->irq_enabled) { + return; + } + + s->irq_enabled = enabled; + pci_set_irq(&s->dev, enabled); +} + I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base) { PCIDevice *d = pci_create_simple_multifunction(bus, devfn, true, TYPE_ICH9_SMB_DEVICE); ICH9SMBState *s = ICH9_SMB_DEVICE(d); + s->smb.set_irq = ich9_smb_set_irq; + s->smb.opaque = s; return s->smb.smbus; } diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h index 99d5489e1b..1afa3cfacd 100644 --- a/include/hw/i2c/pm_smbus.h +++ b/include/hw/i2c/pm_smbus.h @@ -23,6 +23,8 @@ typedef struct PMSMBus { /* Set by the user. */ bool i2c_enable; + void (*set_irq)(struct PMSMBus *s, bool enabled); + void *opaque; /* Internally used by pm_smbus. */ -- cgit v1.2.3-55-g7522 From 12bd93c150239020803b1ae08af819cd38e55360 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 20 Aug 2018 15:26:07 -0500 Subject: i2c: pm_smbus: Don't delay host status register busy bit when interrupts are enabled Change 880b1ffe6ec2f0ae "smbus: do not immediately complete commands" changed pm_smbus to delay setting the host busy bit until the status register was read, to work around a bug in AMIBIOS. Unfortunately, when interrupts are enabled, the status register will never get read and the processing will never happen. Modify the code to only delay setting the host busy bit if interrupts are not enabled. Signed-off-by: Corey Minyard Cc: Hervé Poussineau Cc: Philippe Mathieu-Daudé Message-Id: <1534796770-10295-8-git-send-email-minyard@acm.org> Signed-off-by: Paolo Bonzini --- hw/i2c/pm_smbus.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'hw/i2c') diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 6322f070be..91ee444590 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -80,9 +80,6 @@ static void smb_transaction(PMSMBus *s) I2CBus *bus = s->smbus; int ret; - assert(s->smb_stat & STS_HOST_BUSY); - s->smb_stat &= ~STS_HOST_BUSY; - SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); /* Transaction isn't exec if STS_DEV_ERR bit set */ if ((s->smb_stat & STS_DEV_ERR) != 0) { @@ -209,9 +206,18 @@ error: static void smb_transaction_start(PMSMBus *s) { - /* Do not execute immediately the command ; it will be - * executed when guest will read SMB_STAT register */ - s->smb_stat |= STS_HOST_BUSY; + if (s->smb_ctl & CTL_INTREN) { + smb_transaction(s); + } else { + /* Do not execute immediately the command; it will be + * executed when guest will read SMB_STAT register. This + * is to work around a bug in AMIBIOS (that is working + * around another bug in some specific hardware) where + * it waits for STS_HOST_BUSY to be set before waiting + * checking for status. If STS_HOST_BUSY doesn't get + * set, it gets stuck. */ + s->smb_stat |= STS_HOST_BUSY; + } } static bool @@ -330,6 +336,7 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) val = s->smb_stat; if (s->smb_stat & STS_HOST_BUSY) { /* execute command now */ + s->smb_stat &= ~STS_HOST_BUSY; smb_transaction(s); } break; -- cgit v1.2.3-55-g7522 From 45726b6e2c6075826cacd87f7bdde372589b7cf3 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 20 Aug 2018 15:26:08 -0500 Subject: i2c: pm_smbus: Add the ability to force block transfer enable The PIIX4 hardware has block transfer buffer always enabled in the hardware, but the i801 does not. Add a parameter to pm_smbus_init to force on the block transfer so the PIIX4 handler can enable this by default, as it was disabled by default before. Signed-off-by: Corey Minyard Cc: Michael S. Tsirkin Cc: Paolo Bonzini Message-Id: <1534796770-10295-9-git-send-email-minyard@acm.org> Signed-off-by: Paolo Bonzini --- hw/acpi/piix4.c | 2 +- hw/i2c/pm_smbus.c | 5 ++++- hw/i2c/smbus_ich9.c | 2 +- hw/isa/vt82c686.c | 2 +- include/hw/i2c/pm_smbus.h | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) (limited to 'hw/i2c') diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 6404af5f33..e330f24c71 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -512,7 +512,7 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp) pci_conf[0x90] = s->smb_io_base | 1; pci_conf[0x91] = s->smb_io_base >> 8; pci_conf[0xd2] = 0x09; - pm_smbus_init(DEVICE(dev), &s->smb); + pm_smbus_init(DEVICE(dev), &s->smb, true); memory_region_set_enabled(&s->smb.io, pci_conf[0xd2] & 1); memory_region_add_subregion(pci_address_space_io(dev), s->smb_io_base, &s->smb.io); diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 91ee444590..685a2378ed 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -402,11 +402,14 @@ static const MemoryRegionOps pm_smbus_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -void pm_smbus_init(DeviceState *parent, PMSMBus *smb) +void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk) { smb->op_done = true; smb->reset = pm_smbus_reset; smb->smbus = i2c_init_bus(parent, "i2c"); + if (force_aux_blk) { + smb->smb_auxctl |= AUX_BLK; + } memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb, "pm-smbus", 64); } diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 522a703c26..2a8b49e02f 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -86,7 +86,7 @@ static void ich9_smbus_realize(PCIDevice *d, Error **errp) pci_set_byte(d->config + ICH9_SMB_HOSTC, 0); /* TODO bar0, bar1: 64bit BAR support*/ - pm_smbus_init(&d->qdev, &s->smb); + pm_smbus_init(&d->qdev, &s->smb, false); pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO, &s->smb.io); } diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index cff1946232..7302f6d74b 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -370,7 +370,7 @@ static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp) pci_conf[0x90] = s->smb_io_base | 1; pci_conf[0x91] = s->smb_io_base >> 8; pci_conf[0xd2] = 0x90; - pm_smbus_init(&s->dev.qdev, &s->smb); + pm_smbus_init(&s->dev.qdev, &s->smb, false); memory_region_add_subregion(get_system_io(), s->smb_io_base, &s->smb.io); apm_init(dev, &s->apm, NULL, s); diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h index 1afa3cfacd..060d3c6ac0 100644 --- a/include/hw/i2c/pm_smbus.h +++ b/include/hw/i2c/pm_smbus.h @@ -33,6 +33,6 @@ typedef struct PMSMBus { bool op_done; } PMSMBus; -void pm_smbus_init(DeviceState *parent, PMSMBus *smb); +void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk); #endif /* PM_SMBUS_H */ -- cgit v1.2.3-55-g7522