From patchwork Wed Sep 4 20:07:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jae Hyun Yoo X-Patchwork-Id: 1157972 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.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 46Nw0h3GXsz9sDB for ; Thu, 5 Sep 2019 06:10:12 +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 46Nw0h10WVzDqwm for ; Thu, 5 Sep 2019 06:10:12 +1000 (AEST) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (helo) smtp.helo=mga12.intel.com (client-ip=192.55.52.136; helo=mga12.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 mga12.intel.com (mga12.intel.com [192.55.52.136]) (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 46NvyH4jt3zDqlL for ; Thu, 5 Sep 2019 06:08:07 +1000 (AEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 04 Sep 2019 13:08:01 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.64,468,1559545200"; d="scan'208";a="185217616" Received: from maru.jf.intel.com ([10.54.51.77]) by orsmga003.jf.intel.com with ESMTP; 04 Sep 2019 13:08:01 -0700 From: Jae Hyun Yoo To: Brendan Higgins , Benjamin Herrenschmidt , Joel Stanley , Andrew Jeffery , Ryan Chen , Tao Ren Subject: [PATCH dev-5.2 1/2] dt-bindings: i2c: aspeed: add hardware timeout support Date: Wed, 4 Sep 2019 13:07:57 -0700 Message-Id: <20190904200758.5420-2-jae.hyun.yoo@linux.intel.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20190904200758.5420-1-jae.hyun.yoo@linux.intel.com> References: <20190904200758.5420-1-jae.hyun.yoo@linux.intel.com> MIME-Version: 1.0 X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Jae Hyun Yoo Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Append a binding to support hardware timeout feature. Signed-off-by: Jae Hyun Yoo --- Documentation/devicetree/bindings/i2c/i2c-aspeed.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt index e5b46885c15e..71de956ffa4f 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt @@ -29,6 +29,8 @@ Optional Properties: DMA mode instead of PIO or FIFO respectively, I2C can't use DMA mode. IF both DMA and buffer modes are enabled, DMA mode will be selected. +- aspeed,hw-timeout-ms : Hardware timeout in milliseconds. If it's not + specified, the H/W timeout feature will be disabled. Example: From patchwork Wed Sep 4 20:07:58 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jae Hyun Yoo X-Patchwork-Id: 1157974 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.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 46Nw2D5x8sz9sDB for ; Thu, 5 Sep 2019 06:11:32 +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 46Nw2D44SKzDqq3 for ; Thu, 5 Sep 2019 06:11:32 +1000 (AEST) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (helo) smtp.helo=mga12.intel.com (client-ip=192.55.52.136; helo=mga12.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 mga12.intel.com (mga12.intel.com [192.55.52.136]) (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 46NvyJ5M7dzDqlL for ; Thu, 5 Sep 2019 06:08:08 +1000 (AEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 04 Sep 2019 13:08:02 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.64,468,1559545200"; d="scan'208";a="185217621" Received: from maru.jf.intel.com ([10.54.51.77]) by orsmga003.jf.intel.com with ESMTP; 04 Sep 2019 13:08:02 -0700 From: Jae Hyun Yoo To: Brendan Higgins , Benjamin Herrenschmidt , Joel Stanley , Andrew Jeffery , Ryan Chen , Tao Ren Subject: [PATCH dev-5.2 2/2] i2c: aspeed: add slave inactive timeout support Date: Wed, 4 Sep 2019 13:07:58 -0700 Message-Id: <20190904200758.5420-3-jae.hyun.yoo@linux.intel.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20190904200758.5420-1-jae.hyun.yoo@linux.intel.com> References: <20190904200758.5420-1-jae.hyun.yoo@linux.intel.com> MIME-Version: 1.0 X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Jae Hyun Yoo Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" In case of multi-master environment, if a peer master incorrectly handles a bus in the middle of a transaction, I2C hardware hangs in slave state and it can't escape from the slave state, so this commit adds slave inactive timeout support to recover the bus in the case. Signed-off-by: Jae Hyun Yoo --- drivers/i2c/busses/i2c-aspeed.c | 79 ++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 89317929bee4..92e1a249b393 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -70,10 +70,14 @@ #define ASPEED_I2CD_TIME_SCL_HIGH_MASK GENMASK(19, 16) #define ASPEED_I2CD_TIME_SCL_LOW_SHIFT 12 #define ASPEED_I2CD_TIME_SCL_LOW_MASK GENMASK(15, 12) +#define ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_SHIFT 8 +#define ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_MASK GENMASK(9, 8) #define ASPEED_I2CD_TIME_BASE_DIVISOR_MASK GENMASK(3, 0) #define ASPEED_I2CD_TIME_SCL_REG_MAX GENMASK(3, 0) + /* 0x08 : I2CD Clock and AC Timing Control Register #2 */ -#define ASPEED_NO_TIMEOUT_CTRL 0 +#define ASPEED_I2CD_TIMEOUT_CYCLES_SHIFT 0 +#define ASPEED_I2CD_TIMEOUT_CYCLES_MASK GENMASK(4, 0) /* 0x0c : I2CD Interrupt Control Register & * 0x10 : I2CD Interrupt Status Register @@ -81,6 +85,7 @@ * These share bit definitions, so use the same values for the enable & * status bits. */ +#define ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT BIT(15) #define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14) #define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13) #define ASPEED_I2CD_INTR_SLAVE_MATCH BIT(7) @@ -96,8 +101,11 @@ ASPEED_I2CD_INTR_SCL_TIMEOUT | \ ASPEED_I2CD_INTR_ABNORMAL | \ ASPEED_I2CD_INTR_ARBIT_LOSS) +#define ASPEED_I2CD_INTR_SLAVE_ERRORS \ + ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT #define ASPEED_I2CD_INTR_ALL \ - (ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | \ + (ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT | \ + ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | \ ASPEED_I2CD_INTR_BUS_RECOVER_DONE | \ ASPEED_I2CD_INTR_SCL_TIMEOUT | \ ASPEED_I2CD_INTR_ABNORMAL | \ @@ -176,6 +184,7 @@ struct aspeed_i2c_bus { u32 divisor); unsigned long parent_clk_frequency; u32 bus_frequency; + u32 hw_timeout_ms; /* Transaction state. */ enum aspeed_i2c_master_state master_state; struct i2c_msg *msgs; @@ -276,6 +285,14 @@ static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus) } #if IS_ENABLED(CONFIG_I2C_SLAVE) +static int aspeed_i2c_check_slave_error(u32 irq_status) +{ + if (irq_status & ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT) + return -EIO; + + return 0; +} + static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) { u32 command, irq_handled = 0; @@ -286,6 +303,14 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) if (!slave) return 0; + if (aspeed_i2c_check_slave_error(irq_status)) { + dev_dbg(bus->dev, "received slave error interrupt: 0x%08x\n", + irq_status); + irq_handled |= (irq_status & ASPEED_I2CD_INTR_SLAVE_ERRORS); + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; + return irq_handled; + } + command = readl(bus->base + ASPEED_I2C_CMD_REG); /* Slave was requested, restart state machine. */ @@ -602,7 +627,7 @@ static void aspeed_i2c_next_msg_or_stop(struct aspeed_i2c_bus *bus) } } -static int aspeed_i2c_is_irq_error(u32 irq_status) +static int aspeed_i2c_check_master_error(u32 irq_status) { if (irq_status & ASPEED_I2CD_INTR_ARBIT_LOSS) return -EAGAIN; @@ -633,9 +658,9 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) * should clear the command queue effectively taking us back to the * INACTIVE state. */ - ret = aspeed_i2c_is_irq_error(irq_status); + ret = aspeed_i2c_check_master_error(irq_status); if (ret) { - dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", + dev_dbg(bus->dev, "received master error interrupt: 0x%08x\n", irq_status); irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { @@ -1194,6 +1219,7 @@ static u32 aspeed_i2c_25xx_get_clk_reg_val(struct device *dev, u32 divisor) /* precondition: bus.lock has been acquired. */ static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus) { + u32 timeout_base_divisor, timeout_tick_us, timeout_cycles; u32 divisor, clk_reg_val; divisor = DIV_ROUND_UP(bus->parent_clk_frequency, bus->bus_frequency); @@ -1202,8 +1228,46 @@ static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus) ASPEED_I2CD_TIME_THDSTA_MASK | ASPEED_I2CD_TIME_TACST_MASK); clk_reg_val |= bus->get_clk_reg_val(bus->dev, divisor); + + if (bus->hw_timeout_ms) { + u8 div_max = ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_MASK >> + ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_SHIFT; + u8 cycles_max = ASPEED_I2CD_TIMEOUT_CYCLES_MASK >> + ASPEED_I2CD_TIMEOUT_CYCLES_SHIFT; + + timeout_base_divisor = 0; + + do { + timeout_tick_us = 1000 * (16384 << + (timeout_base_divisor << 1)) / + (bus->parent_clk_frequency / 1000); + + if (timeout_base_divisor == div_max || + timeout_tick_us * ASPEED_I2CD_TIMEOUT_CYCLES_MASK >= + bus->hw_timeout_ms * 1000) + break; + } while (timeout_base_divisor++ < div_max); + + if (timeout_tick_us) { + timeout_cycles = DIV_ROUND_UP(bus->hw_timeout_ms * 1000, + timeout_tick_us); + if (timeout_cycles == 0) + timeout_cycles = 1; + else if (timeout_cycles > cycles_max) + timeout_cycles = cycles_max; + } else { + timeout_cycles = 0; + } + } else { + timeout_base_divisor = 0; + timeout_cycles = 0; + } + + clk_reg_val |= FIELD_PREP(ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_MASK, + timeout_base_divisor); + writel(clk_reg_val, bus->base + ASPEED_I2C_AC_TIMING_REG1); - writel(ASPEED_NO_TIMEOUT_CTRL, bus->base + ASPEED_I2C_AC_TIMING_REG2); + writel(timeout_cycles, bus->base + ASPEED_I2C_AC_TIMING_REG2); return 0; } @@ -1404,6 +1468,9 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) } } + device_property_read_u32(&pdev->dev, "aspeed,hw-timeout-ms", + &bus->hw_timeout_ms); + /* Initialize the I2C adapter */ spin_lock_init(&bus->lock); init_completion(&bus->cmd_complete);