[PATCH i2c-next v6] i2c: aspeed: Handle master/slave combined irq events properly
From: clg@kaod.org (Cédric Le Goater)
Date: 2018-09-13 17:35:46
Also in:
linux-aspeed, linux-i2c, lkml, openbmc
Subsystem:
the rest · Maintainer:
Linus Torvalds
On 09/13/2018 03:33 PM, Guenter Roeck wrote:
On 09/12/2018 10:45 PM, C?dric Le Goater wrote [ ... ]quoted
quoted
--- qemu:diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index c762c73..0d4aa08 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c@@ -180,6 +180,33 @@ static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus)????? return (bus->cmd >> I2CD_TX_STATE_SHIFT) & I2CD_TX_STATE_MASK; ? } ? +static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus) +{ +??? int ret; + +??? if (!(bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST))) { +??????? return; +??? }it deserves a comment to understand which scenario we are trying to handle.quoted
+??? if (bus->intr_status & I2CD_INTR_RX_DONE) { +??????? return; +??? }should be handled in aspeed_i2c_bus_handle_cmd() I thinkI moved those two checks into the calling code.
ok
quoted
quoted
+??? aspeed_i2c_set_state(bus, I2CD_MRXD); +??? ret = i2c_recv(bus->bus); +??? if (ret < 0) { +??????? qemu_log_mask(LOG_GUEST_ERROR, "%s: read failed\n", __func__); +??????? ret = 0xff; +??? } else { +??????? bus->intr_status |= I2CD_INTR_RX_DONE; +??? } +??? bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; +??? if (bus->cmd & I2CD_M_S_RX_CMD_LAST) { +??????? i2c_nack(bus->bus); +??? } +??? bus->cmd &= ~(I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST); +??? aspeed_i2c_set_state(bus, I2CD_MACTIVE); +} + ? /* ?? * The state machine needs some refinement. It is only used to track ?? * invalid STOP commands for the moment.@@ -188,7 +215,7 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value)? { ????? bus->cmd &= ~0xFFFF; ????? bus->cmd |= value & 0xFFFF; -??? bus->intr_status = 0;> +??? bus->intr_status &= I2CD_INTR_RX_DONE;it deserves a comment to understand which scenario we are trying to handle. ??Ok. FWIW, I wonder if intr_status should be touched here in the first place, but I neither have the hardware nor a datasheet, so I don't know if any bits are auto-cleared.
I just pushed a patch on my branch with some more explanation : https://github.com/legoater/qemu/commits/aspeed-3.1
quoted
quoted
????? if (bus->cmd & I2CD_M_START_CMD) { ????????? uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ?@@ -227,22 +254,7 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value)????? } ? ????? if (bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST)) { -??????? int ret; - -??????? aspeed_i2c_set_state(bus, I2CD_MRXD); -??????? ret = i2c_recv(bus->bus); -??????? if (ret < 0) { -??????????? qemu_log_mask(LOG_GUEST_ERROR, "%s: read failed\n", __func__); -??????????? ret = 0xff; -??????? } else { -??????????? bus->intr_status |= I2CD_INTR_RX_DONE; -??????? } -??????? bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; -??????? if (bus->cmd & I2CD_M_S_RX_CMD_LAST) { -??????????? i2c_nack(bus->bus); -??????? } -??????? bus->cmd &= ~(I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST); -??????? aspeed_i2c_set_state(bus, I2CD_MACTIVE); +??????? aspeed_i2c_handle_rx_cmd(bus); ????? } ? ????? if (bus->cmd & I2CD_M_STOP_CMD) {@@ -263,6 +275,7 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset,?????????????????????????????????? uint64_t value, unsigned size) ? { ????? AspeedI2CBus *bus = opaque; +??? int status; ? ????? switch (offset) { ????? case I2CD_FUN_CTRL_REG:@@ -283,9 +296,16 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset,????????? bus->intr_ctrl = value & 0x7FFF; ????????? break; ????? case I2CD_INTR_STS_REG: +??????? status = bus->intr_status; ????????? bus->intr_status &= ~(value & 0x7FFF); -??????? bus->controller->intr_status &= ~(1 << bus->id); -??????? qemu_irq_lower(bus->controller->irq); +??????? if (!bus->intr_status) { +??????????? bus->controller->intr_status &= ~(1 << bus->id); +??????????? qemu_irq_lower(bus->controller->irq); +??????? }That part below is indeed something to fix. I had a similar patch.Should I split it out as separate patch ? Alternatively, if you submitted your patch already, I'll be happy to use it as base for mine.
See below. Thanks a lot, C.
From dea796e8d35ba7a70e4b14fbc30ff970a347b195 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org> Date: Thu, 13 Sep 2018 17:44:32 +0200 Subject: [PATCH] aspeed/i2c: lower bus interrupt when all interrupts have been cleared MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also include some documentation on the interrupt status bits and how they should be cleared. Also, the model does not implement correctly the RX_DONE bit behavior which should be cleared to allow more data to be received. Yet to be fixed. Signed-off-by: C?dric Le Goater <clg@kaod.org> --- hw/i2c/aspeed_i2c.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c
index c762c7366ad9..275377c2ab38 100644
--- a/hw/i2c/aspeed_i2c.c
+++ b/hw/i2c/aspeed_i2c.c@@ -52,6 +52,13 @@ #define I2CD_AC_TIMING_REG2 0x08 /* Clock and AC Timing Control #1 */ #define I2CD_INTR_CTRL_REG 0x0c /* I2CD Interrupt Control */ #define I2CD_INTR_STS_REG 0x10 /* I2CD Interrupt Status */ + +#define I2CD_INTR_SLAVE_ADDR_MATCH (0x1 << 31) /* 0: addr1 1: addr2 */ +#define I2CD_INTR_SLAVE_ADDR_RX_PENDING (0x1 << 30) +/* bits[19-16] Reserved */ + +/* All bits below are cleared by writing 1 */ +#define I2CD_INTR_SLAVE_INACTIVE_TIMEOUT (0x1 << 15) #define I2CD_INTR_SDA_DL_TIMEOUT (0x1 << 14) #define I2CD_INTR_BUS_RECOVER_DONE (0x1 << 13) #define I2CD_INTR_SMBUS_ALERT (0x1 << 12) /* Bus [0-3] only */
@@ -59,11 +66,16 @@ #define I2CD_INTR_SMBUS_DEV_ALERT_ADDR (0x1 << 10) /* Removed */ #define I2CD_INTR_SMBUS_DEF_ADDR (0x1 << 9) /* Removed */ #define I2CD_INTR_GCALL_ADDR (0x1 << 8) /* Removed */ -#define I2CD_INTR_SLAVE_MATCH (0x1 << 7) /* use RX_DONE */ +#define I2CD_INTR_SLAVE_ADDR_RX_MATCH (0x1 << 7) /* use RX_DONE */ #define I2CD_INTR_SCL_TIMEOUT (0x1 << 6) #define I2CD_INTR_ABNORMAL (0x1 << 5) #define I2CD_INTR_NORMAL_STOP (0x1 << 4) #define I2CD_INTR_ARBIT_LOSS (0x1 << 3) + +/* + * TODO: handle correctly I2CD_INTR_RX_DONE which needs to be cleared + * to allow next data to be received. + */ #define I2CD_INTR_RX_DONE (0x1 << 2) #define I2CD_INTR_TX_NAK (0x1 << 1) #define I2CD_INTR_TX_ACK (0x1 << 0)
@@ -284,8 +296,10 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, break; case I2CD_INTR_STS_REG: bus->intr_status &= ~(value & 0x7FFF); - bus->controller->intr_status &= ~(1 << bus->id); - qemu_irq_lower(bus->controller->irq); + if (!bus->intr_status) { + bus->controller->intr_status &= ~(1 << bus->id); + qemu_irq_lower(bus->controller->irq); + } break; case I2CD_DEV_ADDR_REG: qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n",
--
2.17.1