summaryrefslogtreecommitdiffstats
path: root/hw/i2c
diff options
context:
space:
mode:
authorRichard Henderson2022-06-30 18:34:12 +0200
committerRichard Henderson2022-06-30 18:34:12 +0200
commitd495e432c04a6394126c35cf96517749708b410f (patch)
tree847312bcb99b4f486ed20bae9dfd4aaf76bef4fc /hw/i2c
parentMerge tag 'trivial-branch-for-7.1-pull-request' of https://gitlab.com/laurent... (diff)
parenthw/misc/aspeed: Add PECI controller (diff)
downloadqemu-d495e432c04a6394126c35cf96517749708b410f.tar.gz
qemu-d495e432c04a6394126c35cf96517749708b410f.tar.xz
qemu-d495e432c04a6394126c35cf96517749708b410f.zip
Merge tag 'pull-aspeed-20220630' of https://github.com/legoater/qemu into staging
aspeed queue: * m25p80 improvements (Iris) * Code cleanup in preparation of multi SoC machine (Peter) * New MAX31785 model (Mahesh) * New Qualcomm machines (Jae and Graeme) * Core I2C slave mode (Klaus) * Aspeed I2C slave mode for old and new register interface (Peter and Klaus) * New Aspeed PECI model (Peter) * Various small fixes # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmK9UfQACgkQUaNDx8/7 # 7KFYWhAAtlx3aaEacrn/ONNHjk6G9Fxku56gAbaIiuiaIWNOj3/T2frPsnmbO8x8 # EKrgUYB8i8PFve/fJYA5vZUzIddPTaHkULZ12JQoGVg0L9hDBbizslN5lJWRXoSv # 9r3DF9nahzLKRNvzoBfuKjHDQ2cwHoFgYmKmlYpDcgfmBcl16uzZy8jvxg/Tghur # umH4IJMjeDNz/kLfINoO/m+kuFPVXmbTJNwl8uK5MUVDTgVSqharywWlUizugBVH # StLE+GmBPylTuYXyiOzLTkoGJeeHp3sQ1DmyI4DD83odjnfxa0BGMGDVhD35exXi # 9tLY9FgQ4smATuyN0UGAKZTBmzpI+ov0HMzvH4lUMR8i8daBuEet3RVr/DqkOP4h # LEVTRWTaTJip24ohgw4K/b86pI9nTJWVPGV56eZGYmnqufnvf/upNU65/nCsF/xD # i1TdS+zJWxhjgGEepg9cTmxxUlA4jVNNbl6dvAgS5Jr6Igrd1BlCSXjmyhO3NRPZ # bgOuvCb3RyxAY4+/9wphx2/t5X2VIU6R8EAjnh+7nIgBhOQU5SZ6uefFVYZq8xx+ # IYEDHj3saiRa4FHmyOgeRxRaQj/Vvs83PPti2rPmJuieqiClJmbE+XfTIamoxVIv # 5USlKmMRRVI69MjsjwFi/gOaV/N1EUgcFoYbnvwZ+Md3fg5+70M= # =oUKu # -----END PGP SIGNATURE----- # gpg: Signature made Thu 30 Jun 2022 01:04:12 PM +0530 # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@kaod.org>" [undefined] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * tag 'pull-aspeed-20220630' of https://github.com/legoater/qemu: (27 commits) hw/misc/aspeed: Add PECI controller hw/i2c/aspeed: Add new-registers DMA slave mode RX support hw/i2c/aspeed: add slave device in old register mode hw/i2c: add asynchronous send hw/i2c: support multiple masters hw/i2c/aspeed: Fix MASTER_EN missing error message hw/i2c/aspeed: Fix DMA len write-enable bit handling hw/i2c/aspeed: Fix R_I2CD_FUN_CTRL reference hw/arm/aspeed: firework: add I2C MUXes for VR channels hw/arm/aspeed: firework: Add Thermal Diodes hw/arm/aspeed: Add MAX31785 Fan controllers hw/sensor: add Maxim MAX31785 device hw/i2c: pmbus: Page #255 is valid page for read requests. hw/arm/aspeed: add Qualcomm Firework BMC machine hw/arm/aspeed: add support for the Qualcomm DC-SCM v1 board aspeed: Remove use of qemu_get_cpu aspeed: Map unimplemented devices in SoC memory aspeed: Remove usage of sysbus_mmio_map aspeed: Add memory property to Aspeed SoC aspeed: Set CPU memory property explicitly ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'hw/i2c')
-rw-r--r--hw/i2c/aspeed_i2c.c236
-rw-r--r--hw/i2c/core.c70
-rw-r--r--hw/i2c/pmbus_device.c6
-rw-r--r--hw/i2c/smbus_slave.c4
-rw-r--r--hw/i2c/trace-events2
5 files changed, 283 insertions, 35 deletions
diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c
index 37ae1f2e04..42c6d69b82 100644
--- a/hw/i2c/aspeed_i2c.c
+++ b/hw/i2c/aspeed_i2c.c
@@ -58,7 +58,7 @@ static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus)
ARRAY_FIELD_EX32(bus->regs, I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH) ?
"slave-match|" : "",
SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, NORMAL_STOP) ?
- "normal|" : "",
+ "stop|" : "",
SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, ABNORMAL) ?
"abnormal" : "");
@@ -78,6 +78,18 @@ static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus)
}
}
+static inline void aspeed_i2c_bus_raise_slave_interrupt(AspeedI2CBus *bus)
+{
+ AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller);
+
+ if (!bus->regs[R_I2CS_INTR_STS]) {
+ return;
+ }
+
+ bus->controller->intr_status |= 1 << bus->id;
+ qemu_irq_raise(aic->bus_get_irq(bus));
+}
+
static uint64_t aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset,
unsigned size)
{
@@ -140,8 +152,17 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset,
case A_I2CM_DMA_LEN_STS:
case A_I2CC_DMA_ADDR:
case A_I2CC_DMA_LEN:
+
+ case A_I2CS_DEV_ADDR:
+ case A_I2CS_DMA_RX_ADDR:
+ case A_I2CS_DMA_LEN:
+ case A_I2CS_CMD:
+ case A_I2CS_INTR_CTRL:
+ case A_I2CS_DMA_LEN_STS:
/* Value is already set, don't do anything. */
break;
+ case A_I2CS_INTR_STS:
+ break;
case A_I2CM_CMD:
value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus));
break;
@@ -547,12 +568,7 @@ static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset,
switch (offset) {
case A_I2CC_FUN_CTRL:
- if (SHARED_FIELD_EX32(value, SLAVE_EN)) {
- qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n",
- __func__);
- break;
- }
- bus->regs[R_I2CD_FUN_CTRL] = value & 0x007dc3ff;
+ bus->regs[R_I2CC_FUN_CTRL] = value;
break;
case A_I2CC_AC_TIMING:
bus->regs[R_I2CC_AC_TIMING] = value & 0x1ffff0ff;
@@ -580,6 +596,7 @@ static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset,
bus->controller->intr_status &= ~(1 << bus->id);
qemu_irq_lower(aic->bus_get_irq(bus));
}
+ aspeed_i2c_bus_raise_slave_interrupt(bus);
break;
}
bus->regs[R_I2CM_INTR_STS] &= ~(value & 0xf007f07f);
@@ -601,7 +618,7 @@ static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset,
}
if (!aspeed_i2c_bus_is_master(bus)) {
- qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n",
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Master mode is not enabled\n",
__func__);
break;
}
@@ -644,18 +661,18 @@ static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset,
RX_BUF_LEN) + 1;
break;
case A_I2CM_DMA_LEN:
- w1t = ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, RX_BUF_LEN_W1T) ||
- ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, TX_BUF_LEN_W1T);
+ w1t = FIELD_EX32(value, I2CM_DMA_LEN, RX_BUF_LEN_W1T) ||
+ FIELD_EX32(value, I2CM_DMA_LEN, TX_BUF_LEN_W1T);
/* If none of the w1t bits are set, just write to the reg as normal. */
if (!w1t) {
bus->regs[R_I2CM_DMA_LEN] = value;
break;
}
- if (ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, RX_BUF_LEN_W1T)) {
+ if (FIELD_EX32(value, I2CM_DMA_LEN, RX_BUF_LEN_W1T)) {
ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN, RX_BUF_LEN,
FIELD_EX32(value, I2CM_DMA_LEN, RX_BUF_LEN));
}
- if (ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, TX_BUF_LEN_W1T)) {
+ if (FIELD_EX32(value, I2CM_DMA_LEN, TX_BUF_LEN_W1T)) {
ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN, TX_BUF_LEN,
FIELD_EX32(value, I2CM_DMA_LEN, TX_BUF_LEN));
}
@@ -668,15 +685,53 @@ static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset,
case A_I2CC_DMA_LEN:
/* RO */
break;
- case A_I2CS_DMA_LEN_STS:
- case A_I2CS_DMA_TX_ADDR:
- case A_I2CS_DMA_RX_ADDR:
case A_I2CS_DEV_ADDR:
+ bus->regs[R_I2CS_DEV_ADDR] = value;
+ break;
+ case A_I2CS_DMA_RX_ADDR:
+ bus->regs[R_I2CS_DMA_RX_ADDR] = value;
+ break;
+ case A_I2CS_DMA_LEN:
+ assert(FIELD_EX32(value, I2CS_DMA_LEN, TX_BUF_LEN) == 0);
+ if (FIELD_EX32(value, I2CS_DMA_LEN, RX_BUF_LEN_W1T)) {
+ ARRAY_FIELD_DP32(bus->regs, I2CS_DMA_LEN, RX_BUF_LEN,
+ FIELD_EX32(value, I2CS_DMA_LEN, RX_BUF_LEN));
+ } else {
+ bus->regs[R_I2CS_DMA_LEN] = value;
+ }
+ break;
+ case A_I2CS_CMD:
+ if (FIELD_EX32(value, I2CS_CMD, W1_CTRL)) {
+ bus->regs[R_I2CS_CMD] |= value;
+ } else {
+ bus->regs[R_I2CS_CMD] = value;
+ }
+ i2c_slave_set_address(bus->slave, bus->regs[R_I2CS_DEV_ADDR]);
+ break;
case A_I2CS_INTR_CTRL:
+ bus->regs[R_I2CS_INTR_CTRL] = value;
+ break;
+
case A_I2CS_INTR_STS:
- case A_I2CS_CMD:
- case A_I2CS_DMA_LEN:
- qemu_log_mask(LOG_UNIMP, "%s: Slave mode is not implemented\n",
+ if (ARRAY_FIELD_EX32(bus->regs, I2CS_INTR_CTRL, PKT_CMD_DONE)) {
+ if (ARRAY_FIELD_EX32(bus->regs, I2CS_INTR_STS, PKT_CMD_DONE) &&
+ FIELD_EX32(value, I2CS_INTR_STS, PKT_CMD_DONE)) {
+ bus->regs[R_I2CS_INTR_STS] &= 0xfffc0000;
+ }
+ } else {
+ bus->regs[R_I2CS_INTR_STS] &= ~value;
+ }
+ if (!bus->regs[R_I2CS_INTR_STS]) {
+ bus->controller->intr_status &= ~(1 << bus->id);
+ qemu_irq_lower(aic->bus_get_irq(bus));
+ }
+ aspeed_i2c_bus_raise_interrupt(bus);
+ break;
+ case A_I2CS_DMA_LEN_STS:
+ bus->regs[R_I2CS_DMA_LEN_STS] = 0;
+ break;
+ case A_I2CS_DMA_TX_ADDR:
+ qemu_log_mask(LOG_UNIMP, "%s: Slave mode DMA TX is not implemented\n",
__func__);
break;
default:
@@ -696,9 +751,7 @@ static void aspeed_i2c_bus_old_write(AspeedI2CBus *bus, hwaddr offset,
switch (offset) {
case A_I2CD_FUN_CTRL:
if (SHARED_FIELD_EX32(value, SLAVE_EN)) {
- qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n",
- __func__);
- break;
+ i2c_slave_set_address(bus->slave, bus->regs[R_I2CD_DEV_ADDR]);
}
bus->regs[R_I2CD_FUN_CTRL] = value & 0x0071C3FF;
break;
@@ -719,12 +772,15 @@ static void aspeed_i2c_bus_old_write(AspeedI2CBus *bus, hwaddr offset,
bus->controller->intr_status &= ~(1 << bus->id);
qemu_irq_lower(aic->bus_get_irq(bus));
}
- if (handle_rx && (SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD,
- M_RX_CMD) ||
- SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD,
- M_S_RX_CMD_LAST))) {
- aspeed_i2c_handle_rx_cmd(bus);
- aspeed_i2c_bus_raise_interrupt(bus);
+ if (handle_rx) {
+ if (SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, M_RX_CMD) ||
+ SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD,
+ M_S_RX_CMD_LAST)) {
+ aspeed_i2c_handle_rx_cmd(bus);
+ aspeed_i2c_bus_raise_interrupt(bus);
+ } else if (aspeed_i2c_get_state(bus) == I2CD_STXD) {
+ i2c_ack(bus->bus);
+ }
}
break;
case A_I2CD_DEV_ADDR:
@@ -744,7 +800,7 @@ static void aspeed_i2c_bus_old_write(AspeedI2CBus *bus, hwaddr offset,
}
if (!aspeed_i2c_bus_is_master(bus)) {
- qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n",
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Master mode is not enabled\n",
__func__);
break;
}
@@ -1036,6 +1092,127 @@ static const TypeInfo aspeed_i2c_info = {
.abstract = true,
};
+static int aspeed_i2c_bus_new_slave_event(AspeedI2CBus *bus,
+ enum i2c_event event)
+{
+ switch (event) {
+ case I2C_START_SEND_ASYNC:
+ if (!SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CS_CMD, RX_DMA_EN)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Slave mode RX DMA is not enabled\n", __func__);
+ return -1;
+ }
+ ARRAY_FIELD_DP32(bus->regs, I2CS_DMA_LEN_STS, RX_LEN, 0);
+ bus->regs[R_I2CC_DMA_ADDR] =
+ ARRAY_FIELD_EX32(bus->regs, I2CS_DMA_RX_ADDR, ADDR);
+ bus->regs[R_I2CC_DMA_LEN] =
+ ARRAY_FIELD_EX32(bus->regs, I2CS_DMA_LEN, RX_BUF_LEN) + 1;
+ i2c_ack(bus->bus);
+ break;
+ case I2C_FINISH:
+ ARRAY_FIELD_DP32(bus->regs, I2CS_INTR_STS, PKT_CMD_DONE, 1);
+ ARRAY_FIELD_DP32(bus->regs, I2CS_INTR_STS, SLAVE_ADDR_RX_MATCH, 1);
+ SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CS_INTR_STS, NORMAL_STOP, 1);
+ SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CS_INTR_STS, RX_DONE, 1);
+ aspeed_i2c_bus_raise_slave_interrupt(bus);
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: i2c event %d unimplemented\n",
+ __func__, event);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int aspeed_i2c_bus_slave_event(I2CSlave *slave, enum i2c_event event)
+{
+ BusState *qbus = qdev_get_parent_bus(DEVICE(slave));
+ AspeedI2CBus *bus = ASPEED_I2C_BUS(qbus->parent);
+ uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus);
+ uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus);
+ uint32_t value;
+
+ if (aspeed_i2c_is_new_mode(bus->controller)) {
+ return aspeed_i2c_bus_new_slave_event(bus, event);
+ }
+
+ switch (event) {
+ case I2C_START_SEND_ASYNC:
+ value = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_byte_buf, TX_BUF);
+ SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, value << 1);
+
+ ARRAY_FIELD_DP32(bus->regs, I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH, 1);
+ SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, RX_DONE, 1);
+
+ aspeed_i2c_set_state(bus, I2CD_STXD);
+
+ break;
+
+ case I2C_FINISH:
+ SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, NORMAL_STOP, 1);
+
+ aspeed_i2c_set_state(bus, I2CD_IDLE);
+
+ break;
+
+ default:
+ return -1;
+ }
+
+ aspeed_i2c_bus_raise_interrupt(bus);
+
+ return 0;
+}
+
+static void aspeed_i2c_bus_new_slave_send_async(AspeedI2CBus *bus, uint8_t data)
+{
+ assert(address_space_write(&bus->controller->dram_as,
+ bus->regs[R_I2CC_DMA_ADDR],
+ MEMTXATTRS_UNSPECIFIED, &data, 1) == MEMTX_OK);
+
+ bus->regs[R_I2CC_DMA_ADDR]++;
+ bus->regs[R_I2CC_DMA_LEN]--;
+ ARRAY_FIELD_DP32(bus->regs, I2CS_DMA_LEN_STS, RX_LEN,
+ ARRAY_FIELD_EX32(bus->regs, I2CS_DMA_LEN_STS, RX_LEN) + 1);
+ i2c_ack(bus->bus);
+}
+
+static void aspeed_i2c_bus_slave_send_async(I2CSlave *slave, uint8_t data)
+{
+ BusState *qbus = qdev_get_parent_bus(DEVICE(slave));
+ AspeedI2CBus *bus = ASPEED_I2C_BUS(qbus->parent);
+ uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus);
+ uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus);
+
+ if (aspeed_i2c_is_new_mode(bus->controller)) {
+ return aspeed_i2c_bus_new_slave_send_async(bus, data);
+ }
+
+ SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, data);
+ SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, RX_DONE, 1);
+
+ aspeed_i2c_bus_raise_interrupt(bus);
+}
+
+static void aspeed_i2c_bus_slave_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+
+ dc->desc = "Aspeed I2C Bus Slave";
+
+ sc->event = aspeed_i2c_bus_slave_event;
+ sc->send_async = aspeed_i2c_bus_slave_send_async;
+}
+
+static const TypeInfo aspeed_i2c_bus_slave_info = {
+ .name = TYPE_ASPEED_I2C_BUS_SLAVE,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(AspeedI2CBusSlave),
+ .class_init = aspeed_i2c_bus_slave_class_init,
+};
+
static void aspeed_i2c_bus_reset(DeviceState *dev)
{
AspeedI2CBus *s = ASPEED_I2C_BUS(dev);
@@ -1060,6 +1237,8 @@ static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp)
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
s->bus = i2c_init_bus(dev, name);
+ s->slave = i2c_slave_create_simple(s->bus, TYPE_ASPEED_I2C_BUS_SLAVE,
+ 0xff);
memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i2c_bus_ops,
s, name, aic->reg_size);
@@ -1219,6 +1398,7 @@ static const TypeInfo aspeed_1030_i2c_info = {
static void aspeed_i2c_register_types(void)
{
type_register_static(&aspeed_i2c_bus_info);
+ type_register_static(&aspeed_i2c_bus_slave_info);
type_register_static(&aspeed_i2c_info);
type_register_static(&aspeed_2400_i2c_info);
type_register_static(&aspeed_2500_i2c_info);
diff --git a/hw/i2c/core.c b/hw/i2c/core.c
index d0cb2d32fa..d4ba8146bf 100644
--- a/hw/i2c/core.c
+++ b/hw/i2c/core.c
@@ -13,6 +13,7 @@
#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qemu/module.h"
+#include "qemu/main-loop.h"
#include "trace.h"
#define I2C_BROADCAST 0x00
@@ -62,6 +63,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name)
bus = I2C_BUS(qbus_new(TYPE_I2C_BUS, parent, name));
QLIST_INIT(&bus->current_devs);
+ QSIMPLEQ_INIT(&bus->pending_masters);
vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_i2c_bus, bus);
return bus;
}
@@ -74,7 +76,7 @@ void i2c_slave_set_address(I2CSlave *dev, uint8_t address)
/* Return nonzero if bus is busy. */
int i2c_bus_busy(I2CBus *bus)
{
- return !QLIST_EMPTY(&bus->current_devs);
+ return !QLIST_EMPTY(&bus->current_devs) || bus->bh;
}
bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast,
@@ -159,7 +161,8 @@ static int i2c_do_start_transfer(I2CBus *bus, uint8_t address,
start condition. */
if (sc->event) {
- trace_i2c_event("start", s->address);
+ trace_i2c_event(event == I2C_START_SEND ? "start" : "start_async",
+ s->address);
rv = sc->event(s, event);
if (rv && !bus->broadcast) {
if (bus_scanned) {
@@ -180,6 +183,26 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv)
: I2C_START_SEND);
}
+void i2c_bus_master(I2CBus *bus, QEMUBH *bh)
+{
+ if (i2c_bus_busy(bus)) {
+ I2CPendingMaster *node = g_new(struct I2CPendingMaster, 1);
+ node->bh = bh;
+
+ QSIMPLEQ_INSERT_TAIL(&bus->pending_masters, node, entry);
+
+ return;
+ }
+
+ bus->bh = bh;
+ qemu_bh_schedule(bus->bh);
+}
+
+void i2c_bus_release(I2CBus *bus)
+{
+ bus->bh = NULL;
+}
+
int i2c_start_recv(I2CBus *bus, uint8_t address)
{
return i2c_do_start_transfer(bus, address, I2C_START_RECV);
@@ -190,6 +213,11 @@ int i2c_start_send(I2CBus *bus, uint8_t address)
return i2c_do_start_transfer(bus, address, I2C_START_SEND);
}
+int i2c_start_send_async(I2CBus *bus, uint8_t address)
+{
+ return i2c_do_start_transfer(bus, address, I2C_START_SEND_ASYNC);
+}
+
void i2c_end_transfer(I2CBus *bus)
{
I2CSlaveClass *sc;
@@ -206,6 +234,16 @@ void i2c_end_transfer(I2CBus *bus)
g_free(node);
}
bus->broadcast = false;
+
+ if (!QSIMPLEQ_EMPTY(&bus->pending_masters)) {
+ I2CPendingMaster *node = QSIMPLEQ_FIRST(&bus->pending_masters);
+ bus->bh = node->bh;
+
+ QSIMPLEQ_REMOVE_HEAD(&bus->pending_masters, entry);
+ g_free(node);
+
+ qemu_bh_schedule(bus->bh);
+ }
}
int i2c_send(I2CBus *bus, uint8_t data)
@@ -229,6 +267,23 @@ int i2c_send(I2CBus *bus, uint8_t data)
return ret ? -1 : 0;
}
+int i2c_send_async(I2CBus *bus, uint8_t data)
+{
+ I2CNode *node = QLIST_FIRST(&bus->current_devs);
+ I2CSlave *slave = node->elt;
+ I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(slave);
+
+ if (!sc->send_async) {
+ return -1;
+ }
+
+ trace_i2c_send_async(slave->address, data);
+
+ sc->send_async(slave, data);
+
+ return 0;
+}
+
uint8_t i2c_recv(I2CBus *bus)
{
uint8_t data = 0xff;
@@ -265,6 +320,17 @@ void i2c_nack(I2CBus *bus)
}
}
+void i2c_ack(I2CBus *bus)
+{
+ if (!bus->bh) {
+ return;
+ }
+
+ trace_i2c_ack();
+
+ qemu_bh_schedule(bus->bh);
+}
+
static int i2c_slave_post_load(void *opaque, int version_id)
{
I2CSlave *dev = opaque;
diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 62885fa6a1..749a33af82 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -284,14 +284,10 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
/*
* Reading from all pages will return the value from page 0,
- * this is unspecified behaviour in general.
+ * means that all subsequent commands are to be applied to all output.
*/
if (pmdev->page == PB_ALL_PAGES) {
index = 0;
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: tried to read from all pages\n",
- __func__);
- pmbus_cml_error(pmdev);
} else if (pmdev->page > pmdev->num_pages - 1) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: page %d is out of range\n",
diff --git a/hw/i2c/smbus_slave.c b/hw/i2c/smbus_slave.c
index 5d10e27664..feb3ec6333 100644
--- a/hw/i2c/smbus_slave.c
+++ b/hw/i2c/smbus_slave.c
@@ -143,6 +143,10 @@ static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
dev->mode = SMBUS_CONFUSED;
break;
}
+ break;
+
+ default:
+ return -1;
}
return 0;
diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events
index 209275ed2d..af181d43ee 100644
--- a/hw/i2c/trace-events
+++ b/hw/i2c/trace-events
@@ -4,7 +4,9 @@
i2c_event(const char *event, uint8_t address) "%s(addr:0x%02x)"
i2c_send(uint8_t address, uint8_t data) "send(addr:0x%02x) data:0x%02x"
+i2c_send_async(uint8_t address, uint8_t data) "send_async(addr:0x%02x) data:0x%02x"
i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x"
+i2c_ack(void) ""
# aspeed_i2c.c