diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch | 291 |
1 files changed, 0 insertions, 291 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch deleted file mode 100644 index e2dee0d5b..000000000 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch +++ /dev/null @@ -1,291 +0,0 @@ -From a7ad8d09cdf0ec86612df0714d3e69ee92e6140b Mon Sep 17 00:00:00 2001 -From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> -Date: Tue, 20 Nov 2018 09:30:17 -0800 -Subject: [PATCH] i2c: aspeed: Improve driver to support multi-master use cases - stably - -In multi-master environment, this driver's master cannot know -exactly when peer master sends data to this driver's slave so -cases can be happened that this master tries to send data through -the master_xfer function but slave data from a peer master is still -being processed or slave xfer is started by a peer very after it -queues a master command. - -To prevent state corruption in these cases, this patch adds the -'pending' state of master and its handling code so that the pending -master xfer can be continued after slave mode session. - -Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> ---- - drivers/i2c/busses/i2c-aspeed.c | 119 ++++++++++++++++++++++++++++++---------- - 1 file changed, 91 insertions(+), 28 deletions(-) - -diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c -index 8dc9161ced38..d11b2ea97259 100644 ---- a/drivers/i2c/busses/i2c-aspeed.c -+++ b/drivers/i2c/busses/i2c-aspeed.c -@@ -117,6 +117,7 @@ - - enum aspeed_i2c_master_state { - ASPEED_I2C_MASTER_INACTIVE, -+ ASPEED_I2C_MASTER_PENDING, - ASPEED_I2C_MASTER_START, - ASPEED_I2C_MASTER_TX_FIRST, - ASPEED_I2C_MASTER_TX, -@@ -126,12 +127,13 @@ enum aspeed_i2c_master_state { - }; - - enum aspeed_i2c_slave_state { -- ASPEED_I2C_SLAVE_STOP, -+ ASPEED_I2C_SLAVE_INACTIVE, - ASPEED_I2C_SLAVE_START, - ASPEED_I2C_SLAVE_READ_REQUESTED, - ASPEED_I2C_SLAVE_READ_PROCESSED, - ASPEED_I2C_SLAVE_WRITE_REQUESTED, - ASPEED_I2C_SLAVE_WRITE_RECEIVED, -+ ASPEED_I2C_SLAVE_STOP, - }; - - struct aspeed_i2c_bus { -@@ -156,6 +158,8 @@ struct aspeed_i2c_bus { - int cmd_err; - /* Protected only by i2c_lock_bus */ - int master_xfer_result; -+ /* Multi-master */ -+ bool multi_master; - #if IS_ENABLED(CONFIG_I2C_SLAVE) - struct i2c_client *slave; - enum aspeed_i2c_slave_state slave_state; -@@ -251,7 +255,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - } - - /* Slave is not currently active, irq was for someone else. */ -- if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) -+ if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) - return irq_handled; - - dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", -@@ -277,16 +281,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; - bus->slave_state = ASPEED_I2C_SLAVE_STOP; - } -- if (irq_status & ASPEED_I2CD_INTR_TX_NAK) { -+ if (irq_status & ASPEED_I2CD_INTR_TX_NAK && -+ bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { - irq_handled |= ASPEED_I2CD_INTR_TX_NAK; - bus->slave_state = ASPEED_I2C_SLAVE_STOP; - } -- if (irq_status & ASPEED_I2CD_INTR_TX_ACK) -- irq_handled |= ASPEED_I2CD_INTR_TX_ACK; - - switch (bus->slave_state) { - case ASPEED_I2C_SLAVE_READ_REQUESTED: -- if (irq_status & ASPEED_I2CD_INTR_TX_ACK) -+ if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK)) - dev_err(bus->dev, "Unexpected ACK on read request.\n"); - bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED; - i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); -@@ -294,9 +297,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); - break; - case ASPEED_I2C_SLAVE_READ_PROCESSED: -- if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK)) -+ if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { - dev_err(bus->dev, - "Expected ACK after processed read.\n"); -+ break; -+ } -+ irq_handled |= ASPEED_I2CD_INTR_TX_ACK; - i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); - writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); - writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); -@@ -310,10 +316,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - break; - case ASPEED_I2C_SLAVE_STOP: - i2c_slave_event(slave, I2C_SLAVE_STOP, &value); -+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; -+ break; -+ case ASPEED_I2C_SLAVE_START: -+ /* Slave was just started. Waiting for the next event. */; - break; - default: -- dev_err(bus->dev, "unhandled slave_state: %d\n", -+ dev_err(bus->dev, "unknown slave_state: %d\n", - bus->slave_state); -+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; - break; - } - -@@ -328,7 +339,17 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) - struct i2c_msg *msg = &bus->msgs[bus->msgs_index]; - u8 slave_addr = i2c_8bit_addr_from_msg(msg); - -- bus->master_state = ASPEED_I2C_MASTER_START; -+#if IS_ENABLED(CONFIG_I2C_SLAVE) -+ /* -+ * If it's requested in the middle of a slave session, set the master -+ * state to 'pending' then H/W will continue handling this master -+ * command when the bus comes back to idle state. -+ */ -+ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) -+ bus->master_state = ASPEED_I2C_MASTER_PENDING; -+ else -+#endif /* CONFIG_I2C_SLAVE */ -+ bus->master_state = ASPEED_I2C_MASTER_START; - bus->buf_index = 0; - - if (msg->flags & I2C_M_RD) { -@@ -384,10 +405,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - bus->master_state = ASPEED_I2C_MASTER_INACTIVE; - irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE; - goto out_complete; -- } else { -- /* Master is not currently active, irq was for someone else. */ -- if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE) -- goto out_no_complete; - } - - /* -@@ -399,12 +416,33 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - if (ret) { - dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", - irq_status); -- bus->cmd_err = ret; -- bus->master_state = ASPEED_I2C_MASTER_INACTIVE; - irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); -- goto out_complete; -+ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { -+ bus->cmd_err = ret; -+ bus->master_state = ASPEED_I2C_MASTER_INACTIVE; -+ goto out_complete; -+ } - } - -+#if IS_ENABLED(CONFIG_I2C_SLAVE) -+ /* -+ * A pending master command will be started by H/W when the bus comes -+ * back to idle state after completing a slave operation so change the -+ * master state from 'pending' to 'start' at here if slave is inactive. -+ */ -+ if (bus->master_state == ASPEED_I2C_MASTER_PENDING) { -+ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) -+ goto out_no_complete; -+ -+ bus->master_state = ASPEED_I2C_MASTER_START; -+ } -+#endif /* CONFIG_I2C_SLAVE */ -+ -+ /* Master is not currently active, irq was for someone else. */ -+ if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE || -+ bus->master_state == ASPEED_I2C_MASTER_PENDING) -+ goto out_no_complete; -+ - /* We are in an invalid state; reset bus to a known state. */ - if (!bus->msgs) { - dev_err(bus->dev, "bus in unknown state. irq_status: 0x%x\n", -@@ -423,6 +461,20 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - * then update the state and handle the new state below. - */ - if (bus->master_state == ASPEED_I2C_MASTER_START) { -+#if IS_ENABLED(CONFIG_I2C_SLAVE) -+ /* -+ * If a peer master starts a xfer very after it queues a master -+ * command, change its state to 'pending' then H/W will continue -+ * the queued master xfer just after completing the slave mode -+ * session. -+ */ -+ if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) { -+ bus->master_state = ASPEED_I2C_MASTER_PENDING; -+ dev_dbg(bus->dev, -+ "master goes pending due to a slave start\n"); -+ goto out_no_complete; -+ } -+#endif /* CONFIG_I2C_SLAVE */ - if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { - if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) { - bus->cmd_err = -ENXIO; -@@ -566,7 +618,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) - * interrupt bits. Each case needs to be handled using corresponding - * handlers depending on the current state. - */ -- if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { -+ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE && -+ bus->master_state != ASPEED_I2C_MASTER_PENDING) { - irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); - irq_remaining &= ~irq_handled; - if (irq_remaining) -@@ -601,15 +654,14 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, - { - struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); - unsigned long time_left, flags; -- int ret = 0; -+ int ret; - - spin_lock_irqsave(&bus->lock, flags); - bus->cmd_err = 0; - -- /* If bus is busy, attempt recovery. We assume a single master -- * environment. -- */ -- if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) { -+ /* If bus is busy in a single master environment, attempt recovery. */ -+ if (!bus->multi_master && -+ (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS)) { - spin_unlock_irqrestore(&bus->lock, flags); - ret = aspeed_i2c_recover_bus(bus); - if (ret) -@@ -629,10 +681,20 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, - time_left = wait_for_completion_timeout(&bus->cmd_complete, - bus->adap.timeout); - -- if (time_left == 0) -+ if (time_left == 0) { -+ /* -+ * If timed out and bus is still busy in a multi master -+ * environment, attempt recovery at here. -+ */ -+ if (bus->multi_master && -+ (readl(bus->base + ASPEED_I2C_CMD_REG) & -+ ASPEED_I2CD_BUS_BUSY_STS)) -+ ret = aspeed_i2c_recover_bus(bus); -+ - return -ETIMEDOUT; -- else -- return bus->master_xfer_result; -+ } -+ -+ return bus->master_xfer_result; - } - - static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) -@@ -672,7 +734,7 @@ static int aspeed_i2c_reg_slave(struct i2c_client *client) - __aspeed_i2c_reg_slave(bus, client->addr); - - bus->slave = client; -- bus->slave_state = ASPEED_I2C_SLAVE_STOP; -+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; - spin_unlock_irqrestore(&bus->lock, flags); - - return 0; -@@ -827,7 +889,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, - if (ret < 0) - return ret; - -- if (!of_property_read_bool(pdev->dev.of_node, "multi-master")) -+ if (of_property_read_bool(pdev->dev.of_node, "multi-master")) -+ bus->multi_master = true; -+ else - fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS; - - /* Enable Master Mode */ -@@ -930,7 +994,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) - init_completion(&bus->cmd_complete); - bus->adap.owner = THIS_MODULE; - bus->adap.retries = 0; -- bus->adap.timeout = 5 * HZ; - bus->adap.algo = &aspeed_i2c_algo; - bus->adap.dev.parent = &pdev->dev; - bus->adap.dev.of_node = pdev->dev.of_node; --- -2.7.4 - |