diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 71e2ebc63c..4d055806cd 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -696,9 +696,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 +717,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: @@ -1036,6 +1037,73 @@ static const TypeInfo aspeed_i2c_info = { .abstract = true, }; +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; + + 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_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); + + 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 +1128,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 +1289,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/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index 1398befc10..ba148b2f6d 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -223,6 +223,9 @@ struct AspeedI2CBus { struct AspeedI2CState *controller; + /* slave mode */ + I2CSlave *slave; + MemoryRegion mr; I2CBus *bus; @@ -249,6 +252,11 @@ struct AspeedI2CState { AddressSpace dram_as; }; +#define TYPE_ASPEED_I2C_BUS_SLAVE "aspeed.i2c.slave" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedI2CBusSlave, ASPEED_I2C_BUS_SLAVE) +struct AspeedI2CBusSlave { + I2CSlave i2c; +}; struct AspeedI2CClass { SysBusDeviceClass parent_class;