From patchwork Mon Sep 24 23:04:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jae Hyun Yoo X-Patchwork-Id: 974160 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42K0F92t7kz9sB5 for ; Tue, 25 Sep 2018 09:06:21 +1000 (AEST) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.intel.com Received: from bilbo.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 42K0F9178QzF3K5 for ; Tue, 25 Sep 2018 09:06:21 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.intel.com X-Original-To: linux-aspeed@lists.ozlabs.org Delivered-To: linux-aspeed@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=linux.intel.com (client-ip=134.134.136.31; helo=mga06.intel.com; envelope-from=jae.hyun.yoo@linux.intel.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.intel.com Received: from mga06.intel.com (mga06.intel.com [134.134.136.31]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42K0Cp1tCjzF3JJ; Tue, 25 Sep 2018 09:05:10 +1000 (AEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga104.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 24 Sep 2018 16:05:04 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,299,1534834800"; d="scan'208";a="93371996" Received: from maru.jf.intel.com ([10.54.51.77]) by orsmga001.jf.intel.com with ESMTP; 24 Sep 2018 16:04:54 -0700 From: Jae Hyun Yoo To: Brendan Higgins , Benjamin Herrenschmidt , Joel Stanley , Rob Herring , Mark Rutland , Andrew Jeffery , linux-i2c@vger.kernel.org, openbmc@lists.ozlabs.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org Subject: [PATCH i2c-next v2 2/2] i2c: aspeed: Add bus idle waiting logic for multi-master use cases Date: Mon, 24 Sep 2018 16:04:42 -0700 Message-Id: <20180924230442.7732-3-jae.hyun.yoo@linux.intel.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180924230442.7732-1-jae.hyun.yoo@linux.intel.com> References: <20180924230442.7732-1-jae.hyun.yoo@linux.intel.com> X-BeenThere: linux-aspeed@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux ASPEED SoC development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Vernon Mauery , Jarkko Nikula , James Feist Errors-To: linux-aspeed-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Linux-aspeed" In multi-master environment, this driver's master cannot know exactly when peer master sends data to this driver's slave so a case can be happened that this master tries to send data through the master_xfer function but slave data from peer master is still being processed by this driver. To prevent state corruption in the case, this patch adds checking if any slave operation is ongoing and it waits up to the timeout duration before starting a master_xfer operation. Signed-off-by: Jae Hyun Yoo --- drivers/i2c/busses/i2c-aspeed.c | 70 ++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 8dc9161ced38..acc26e2a8866 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -99,6 +100,7 @@ ASPEED_I2CD_INTR_TX_ACK) /* 0x14 : I2CD Command/Status Register */ +#define ASPEED_I2CD_XFER_MODE_STS_MASK GENMASK(22, 19) #define ASPEED_I2CD_SCL_LINE_STS BIT(18) #define ASPEED_I2CD_SDA_LINE_STS BIT(17) #define ASPEED_I2CD_BUS_BUSY_STS BIT(16) @@ -115,6 +117,10 @@ /* 0x18 : I2CD Slave Device Address Register */ #define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0) +/* Bus busy checking */ +#define ASPEED_I2C_IDLE_WAIT_TIMEOUT_MS_DEFAULT 100 /* 100 ms */ +#define ASPEED_I2C_BUS_BUSY_CHECK_INTERVAL_US 10000 /* 10 ms */ + enum aspeed_i2c_master_state { ASPEED_I2C_MASTER_INACTIVE, ASPEED_I2C_MASTER_START, @@ -156,6 +162,9 @@ struct aspeed_i2c_bus { int cmd_err; /* Protected only by i2c_lock_bus */ int master_xfer_result; + /* Multi-master */ + bool multi_master; + u32 idle_wait_timeout_ms; #if IS_ENABLED(CONFIG_I2C_SLAVE) struct i2c_client *slave; enum aspeed_i2c_slave_state slave_state; @@ -596,27 +605,49 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) return irq_remaining ? IRQ_NONE : IRQ_HANDLED; } +static int aspeed_i2c_check_bus_busy(struct aspeed_i2c_bus *bus) +{ + u32 status_check_mask = ASPEED_I2CD_BUS_BUSY_STS; + ktime_t timeout; + + if (bus->multi_master) { + might_sleep(); + timeout = ktime_add_ms(ktime_get(), bus->idle_wait_timeout_ms); + /* + * ASPEED_I2CD_XFER_MODE_STS_MASK is marked as + * 'for debugging purpose only' in datasheet but ASPEED + * confirmed that this reflects real information and good to be + * used in practical code. It will be used only in multi-master + * use cases. + */ + status_check_mask |= ASPEED_I2CD_XFER_MODE_STS_MASK; + } + + for (;;) { + if (!(readl(bus->base + ASPEED_I2C_CMD_REG) & + status_check_mask)) + return 0; + if (!bus->multi_master) + break; + if (ktime_compare(ktime_get(), timeout) > 0) + break; + usleep_range((ASPEED_I2C_BUS_BUSY_CHECK_INTERVAL_US >> 2) + 1, + ASPEED_I2C_BUS_BUSY_CHECK_INTERVAL_US); + } + + return aspeed_i2c_recover_bus(bus); +} + static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); unsigned long time_left, flags; - int ret = 0; - - 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) { - spin_unlock_irqrestore(&bus->lock, flags); - ret = aspeed_i2c_recover_bus(bus); - if (ret) - return ret; - spin_lock_irqsave(&bus->lock, flags); - } + if (aspeed_i2c_check_bus_busy(bus)) + return -EAGAIN; + spin_lock_irqsave(&bus->lock, flags); bus->cmd_err = 0; bus->msgs = msgs; bus->msgs_index = 0; @@ -827,8 +858,17 @@ 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; + ret = of_property_read_u32(pdev->dev.of_node, + "idle-wait-timeout-ms", + &bus->idle_wait_timeout_ms); + if (ret) + bus->idle_wait_timeout_ms = + ASPEED_I2C_IDLE_WAIT_TIMEOUT_MS_DEFAULT; + } else { fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS; + } /* Enable Master Mode */ writel(readl(bus->base + ASPEED_I2C_FUN_CTRL_REG) | fun_ctrl_reg,