Received: by 2002:a05:7412:8d10:b0:f3:1519:9f41 with SMTP id bj16csp5382649rdb; Wed, 13 Dec 2023 07:14:06 -0800 (PST) X-Google-Smtp-Source: AGHT+IFNHeuIQ4hMLO1dKvI86aXPFBohsWzenNI63Ed2BUKs6WRSlSbUK8Ih+8NBW5TDuvinn58l X-Received: by 2002:a17:902:e80c:b0:1d0:a791:7598 with SMTP id u12-20020a170902e80c00b001d0a7917598mr9041348plg.129.1702480446394; Wed, 13 Dec 2023 07:14:06 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1702480446; cv=none; d=google.com; s=arc-20160816; b=a9GI0EP2BnO07QieOYuGNNODtWvDPqW4kZZ9+nk/p0Kx5bASR5WZ7OFZlDmz8dEDCi xApAngJtEHkVyhsMLSD6LEvq48yBfbiE91DlAsVMMi/D94Gkm7JTZdXyXeB3z2/VNwAV iA7ln9/xdkUfBEwiZ/NWQG6C/p2VfN4LdlLC48NqWweYBB3IknN7Z93jLw40xzYf0GVm CxZrWK0cHwQXXX3t1XLI3Qli7EIptwvrmJRNttRvGMe4v7jiNGReYC+M8s2Z4gd6SMA0 IfkR8ZRDZbWCF0RYJ8FyY8mUXYkqYqHPJfH7NoHl+Fv6RThdFWOlRgXywdvtYwsG0Kbs LNCw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=sMtZ9Q01OAK6p2ziuaeGd7XYYB49/WfDTIJFtebrjAs=; fh=TrF4wlLHwPbNRvrizfxZNtbZjmnTJlsY86m5mAbhric=; b=vusYVxOcrx9DPdRuDHNgeZYxGF1ZQREfWGhw0GcblfTAnemPCHMdP90UKCaIzw8apI c/lcsM5y4J30l8Q++ikosIb5mDjsD2H4nLYWvpn4l2zsC46Juv9HEb/+KgkrFKt7dg6O zzJluprT7giEBtFjhaMInecTaC85Rh5Yu0pvx2NrcNj/yFRCZEcxe0OUyqJ3sArrYhfu 8SYzBusOACa7Fvk77QTGWGaTmE6AGzbZfwsw2cqoImnuSLgT/qyCCDn7WFwC+yqf/34A VNb3jqkAwVJSnrZWJc07lo1WfW2oGkkGbh397qYv+nPwnNWY6ZIBD2KquSM+Xat+G1rp vxbg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@marvell.com header.s=pfpt0220 header.b=elFwHyZ2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=REJECT dis=NONE) header.from=marvell.com Return-Path: Received: from pete.vger.email (pete.vger.email. [2620:137:e000::3:6]) by mx.google.com with ESMTPS id d9-20020a170903230900b001d06819a68esi9959058plh.134.2023.12.13.07.14.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 Dec 2023 07:14:06 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) client-ip=2620:137:e000::3:6; Authentication-Results: mx.google.com; dkim=pass header.i=@marvell.com header.s=pfpt0220 header.b=elFwHyZ2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=REJECT dis=NONE) header.from=marvell.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by pete.vger.email (Postfix) with ESMTP id 272638199706; Wed, 13 Dec 2023 07:14:03 -0800 (PST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.11 at pete.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1442278AbjLMPNe (ORCPT + 99 others); Wed, 13 Dec 2023 10:13:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35378 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1442280AbjLMPNb (ORCPT ); Wed, 13 Dec 2023 10:13:31 -0500 Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 32701E8; Wed, 13 Dec 2023 07:13:37 -0800 (PST) Received: from pps.filterd (m0045851.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.17.1.24/8.17.1.24) with ESMTP id 3BDAGk1R026392; Wed, 13 Dec 2023 07:13:26 -0800 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h= from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding:content-type; s= pfpt0220; bh=sMtZ9Q01OAK6p2ziuaeGd7XYYB49/WfDTIJFtebrjAs=; b=elF wHyZ298smuVUfRHmOt8MZFKoQPjeVNCl9JRA4wxSgdyF3+PDrJZfulZfHalCZM9t Vs2/agV2p7mdbWmNGeA/NWQEi2ZRPwMrGRieAy4WUONcDMIL0wGzIcRMNhkep/CI +uJTGaZaOt5H/94f1zY2BBsRKQaveWPq+hvpgqvj0WEqUspX4GUfxVV6SZeyG5GD p3EUDfm1iT6V2kSKpc783wL/O5rqMU2G42kg/AHi9UErtwjnc2qiYFdQF+qjHoFN YqPhUasX7dFZTd40SpFKtbvpMKtcqDWLV38s1pjh9eiVDuINF/y+PFcdEJSPbRio ZhFqz2K+qKzpeQEtPwA== Received: from dc5-exch02.marvell.com ([199.233.59.182]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 3uvrmjxb4q-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Wed, 13 Dec 2023 07:13:26 -0800 (PST) Received: from DC5-EXCH01.marvell.com (10.69.176.38) by DC5-EXCH02.marvell.com (10.69.176.39) with Microsoft SMTP Server (TLS) id 15.0.1497.48; Wed, 13 Dec 2023 07:13:24 -0800 Received: from maili.marvell.com (10.69.176.80) by DC5-EXCH01.marvell.com (10.69.176.38) with Microsoft SMTP Server id 15.0.1497.48 via Frontend Transport; Wed, 13 Dec 2023 07:13:24 -0800 Received: from dc3lp-swdev041.marvell.com (dc3lp-swdev041.marvell.com [10.6.60.191]) by maili.marvell.com (Postfix) with ESMTP id F04315B6942; Wed, 13 Dec 2023 07:13:22 -0800 (PST) From: Elad Nachman To: , , , CC: , Subject: [PATCH v3 1/1] i2c: busses: i2c-mv64xxx: fix arb-loss i2c lock Date: Wed, 13 Dec 2023 17:13:12 +0200 Message-ID: <20231213151312.1165115-2-enachman@marvell.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231213151312.1165115-1-enachman@marvell.com> References: <20231213151312.1165115-1-enachman@marvell.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-Proofpoint-GUID: JYEzZpHpn5zvwED_DEEpxvkzl7FFK0K5 X-Proofpoint-ORIG-GUID: JYEzZpHpn5zvwED_DEEpxvkzl7FFK0K5 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.997,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2023-12-09_02,2023-12-07_01,2023-05-22_02 X-Spam-Status: No, score=-0.9 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on pete.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (pete.vger.email [0.0.0.0]); Wed, 13 Dec 2023 07:14:03 -0800 (PST) From: Elad Nachman Some i2c slaves, mainly SFPs, might cause the bus to lose arbitration while slave is in the middle of responding. This means that the i2c slave has not finished the transmission, but the master has already finished toggling the clock, probably due to the slave missing some of the master's clocks. This was seen with Ubiquity SFP module. This is typically caused by slaves which do not adhere completely to the i2c standard. The solution is to change the I2C mode from mpps to gpios, and toggle the i2c_scl gpio to emulate bus clock toggling, so slave will finish its transmission, driven by the manual clock toggling, and will release the i2c bus. Signed-off-by: Elad Nachman --- drivers/i2c/busses/i2c-mv64xxx.c | 118 ++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index dc160cbc3155..9d80cb393283 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -26,6 +26,7 @@ #include #include #include +#include #define MV64XXX_I2C_ADDR_ADDR(val) ((val & 0x7f) << 1) #define MV64XXX_I2C_BAUD_DIV_N(val) (val & 0x7) @@ -104,6 +105,7 @@ enum { MV64XXX_I2C_ACTION_RCV_DATA, MV64XXX_I2C_ACTION_RCV_DATA_STOP, MV64XXX_I2C_ACTION_SEND_STOP, + MV64XXX_I2C_ACTION_UNLOCK_BUS }; struct mv64xxx_i2c_regs { @@ -150,6 +152,12 @@ struct mv64xxx_i2c_data { bool clk_n_base_0; struct i2c_bus_recovery_info rinfo; bool atomic; + /* I2C mpp states & gpios needed for arbitration lost recovery */ + int scl_gpio, sda_gpio; + bool soft_reset; + struct pinctrl_state *i2c_mpp_state; + struct pinctrl_state *i2c_gpio_state; + struct pinctrl *pc; }; static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = { @@ -318,6 +326,11 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status) drv_data->state = MV64XXX_I2C_STATE_IDLE; break; + case MV64XXX_I2C_STATUS_MAST_LOST_ARB: /* 0x38 */ + drv_data->action = MV64XXX_I2C_ACTION_UNLOCK_BUS; + drv_data->state = MV64XXX_I2C_STATE_IDLE; + break; + case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */ case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */ case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */ @@ -356,6 +369,9 @@ static void mv64xxx_i2c_send_start(struct mv64xxx_i2c_data *drv_data) static void mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) { + struct pinctrl *pc; + int i; + switch(drv_data->action) { case MV64XXX_I2C_ACTION_SEND_RESTART: /* We should only get here if we have further messages */ @@ -409,6 +425,54 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) drv_data->reg_base + drv_data->reg_offsets.control); break; + case MV64XXX_I2C_ACTION_UNLOCK_BUS: + if (!drv_data->soft_reset) + break; + + pc = drv_data->pc; + if (IS_ERR(pc)) { + dev_err(&drv_data->adapter.dev, + "failed to get pinctrl for bus unlock!\n"); + break; + } + + /* Change i2c MPPs state to act as GPIOs: */ + if (pinctrl_select_state(pc, drv_data->i2c_gpio_state) >= 0) { + /* + * Toggle i2c scl (serial clock) 10 times. + * 10 clocks are enough to transfer a full + * byte plus extra as seen from tests with + * Ubiquity SFP module causing the issue. + * This allows the slave that occupies + * the bus to transmit its remaining data, + * so it can release the i2c bus: + */ + for (i = 0; i < 10; i++) { + gpio_set_value(drv_data->scl_gpio, 1); + usleep_range(100, 1000); + gpio_set_value(drv_data->scl_gpio, 0); + }; + + /* restore i2c pin state to MPPs: */ + pinctrl_select_state(pc, drv_data->i2c_mpp_state); + } + + /* + * Trigger controller soft reset + * This register is write only, with none of the bits defined. + * So any value will do. + * 0x1 just seems more intuitive than 0x0 ... + */ + writel(0x1, drv_data->reg_base + drv_data->reg_offsets.soft_reset); + /* wait for i2c controller to complete reset: */ + usleep_range(100, 1000); + /* + * Need to proceed to the data stop condition generation clause. + * This is needed after clock toggling to put the i2c slave + * in the correct state. + */ + fallthrough; + case MV64XXX_I2C_ACTION_RCV_DATA_STOP: drv_data->msg->buf[drv_data->byte_posn++] = readl(drv_data->reg_base + drv_data->reg_offsets.data); @@ -985,6 +1049,7 @@ mv64xxx_i2c_probe(struct platform_device *pd) { struct mv64xxx_i2c_data *drv_data; struct mv64xxx_i2c_pdata *pdata = dev_get_platdata(&pd->dev); + struct pinctrl *pc; int rc; if ((!pdata && !pd->dev.of_node)) @@ -1040,6 +1105,47 @@ mv64xxx_i2c_probe(struct platform_device *pd) if (rc == -EPROBE_DEFER) return rc; + /* + * Start with arbitration lost soft reset enabled as to false. + * Try to locate the necessary items in the device tree to + * make this feature work. + * Only after we verify that the device tree contains all of + * the needed information and that it is sound and usable, + * then we enable this flag. + * This information should be defined, but the driver maintains + * backward compatibility with old dts files, so it will not fail + * the probe in case these are missing. + */ + drv_data->soft_reset = false; + pc = pinctrl_get(&pd->dev); + if (!IS_ERR(pc)) { + drv_data->pc = pc; + drv_data->i2c_mpp_state = + pinctrl_lookup_state(pc, "default"); + drv_data->i2c_gpio_state = + pinctrl_lookup_state(pc, "gpio"); + drv_data->scl_gpio = + of_get_named_gpio(pd->dev.of_node, "scl-gpios", 0); + drv_data->sda_gpio = + of_get_named_gpio(pd->dev.of_node, "sda-gpios", 0); + + if (!IS_ERR(drv_data->i2c_gpio_state) && + !IS_ERR(drv_data->i2c_mpp_state) && + gpio_is_valid(drv_data->scl_gpio) && + gpio_is_valid(drv_data->sda_gpio)) { + rc = devm_gpio_request_one(&pd->dev, drv_data->scl_gpio, + GPIOF_DIR_OUT, NULL); + rc |= devm_gpio_request_one(&pd->dev, drv_data->sda_gpio, + GPIOF_DIR_OUT, NULL); + if (!rc) + drv_data->soft_reset = true; + } + } + + if (!drv_data->soft_reset) + dev_info(&pd->dev, + "mv64xxx: missing arbitration-lost recovery definitions in dts file\n"); + drv_data->adapter.dev.parent = &pd->dev; drv_data->adapter.algo = &mv64xxx_i2c_algo; drv_data->adapter.owner = THIS_MODULE; @@ -1079,7 +1185,8 @@ mv64xxx_i2c_probe(struct platform_device *pd) pm_runtime_disable(&pd->dev); if (!pm_runtime_status_suspended(&pd->dev)) mv64xxx_i2c_runtime_suspend(&pd->dev); - + if (!IS_ERR(drv_data->pc)) + pinctrl_put(drv_data->pc); return rc; } @@ -1088,6 +1195,15 @@ mv64xxx_i2c_remove(struct platform_device *pd) { struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(pd); + if (!IS_ERR(drv_data->pc)) + pinctrl_put(drv_data->pc); + if (drv_data->soft_reset) { + devm_gpiod_put(drv_data->adapter.dev.parent, + gpio_to_desc(drv_data->scl_gpio)); + devm_gpiod_put(drv_data->adapter.dev.parent, + gpio_to_desc(drv_data->sda_gpio)); + } + i2c_del_adapter(&drv_data->adapter); free_irq(drv_data->irq, drv_data); pm_runtime_disable(&pd->dev); -- 2.25.1