diff options
Diffstat (limited to 'hw/i2c/aspeed_i2c.c')
-rw-r--r-- | hw/i2c/aspeed_i2c.c | 236 |
1 files changed, 208 insertions, 28 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); |