From nobody Fri Sep 25 03:17:52 2026 Received: from canpmsgout11.his.huawei.com (canpmsgout11.his.huawei.com [113.46.200.226]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2DA6335677E; Thu, 17 Sep 2026 02:40:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.226 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789612839; cv=none; b=Kxd/EaBguHsSSCxMjpUB9b6tJFj1MpJXcb7jIYHcqfo0fjpeReRa1M4no3P2OlkTiZuqzIqBd7fxDsY0W7Tp80PRY6DzUm8o60PtHXgPs04247pIE+vfa0AO8YyIlduFHCxHEVqBncWPBYXAY6rEFnbOD66dQNEcpd37pt0SUy4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789612839; c=relaxed/simple; bh=g5rDKauU/9rz3T8Xd2Jyetuk+qeWn7h3bTY6J7hQpGo=; h=From:To:CC:Subject:Date:Message-ID:MIME-Version:Content-Type; b=mpeD6pK40uVQ6rSbiodnunF8UmNN2bW7c5vkU+ZjT0UgPfpavmSZINt9OdW2BAtLiJC1qzj3n95ni9YgAjnGrVhvWm+E+/HATcM+AatNmC1OLqUHmjx1Sjb22Br5+OfYZU679fsPIGSDqOXLyvrbwFljuinNVu1vL/ZvlqDAcJ4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=2GnM/5Jp; arc=none smtp.client-ip=113.46.200.226 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="2GnM/5Jp" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=Zr54UqN6yelPjhIFhufB4+llcE4Gheof+v5yBL228yM=; b=2GnM/5JpxEyUNHkJDTBwKocZlO9tu0Q6QQO8Xmiuhw2hOyeMP67wwmZhEMpAqw57iqTKSmLT/ dSpL4dYSSDf4JSeZdQgVRPeqiZDiLcPIfHly7pN0hMFxY1a9fj3WsOdUS7fhJtO/piXeXUEswyO I4rO5PsOwuMnW9o4brCVNv0= Received: from mail.maildlp.com (unknown [172.19.162.92]) by canpmsgout11.his.huawei.com (SkyGuard) with ESMTPS id 4hlfpP20kmzKm5t; Thu, 17 Sep 2026 10:29:17 +0800 (CST) Received: from kwepemo100017.china.huawei.com (unknown [7.202.195.215]) by mail.maildlp.com (Postfix) with ESMTPS id 11F824058A; Thu, 17 Sep 2026 10:40:20 +0800 (CST) Received: from localhost.localdomain (10.50.163.32) by kwepemo100017.china.huawei.com (7.202.195.215) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.46; Thu, 17 Sep 2026 10:40:19 +0800 From: Bowen Yu To: , , , , CC: , , , , , Subject: [PATCH v4] i2c: hisi: Add I2C bus recovery support Date: Thu, 17 Sep 2026 10:40:18 +0800 Message-ID: <20260917024018.3052954-1-yubowen8@huawei.com> X-Mailer: git-send-email 2.33.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems500001.china.huawei.com (7.221.188.70) To kwepemo100017.china.huawei.com (7.202.195.215) Content-Type: text/plain; charset="utf-8" When the I2C bus is stuck due to a slave device holding SDA low (e.g. during an incomplete transfer), the master has no way to recover the bus through normal operation. Add bus recovery support using the subctrl register to manually toggle SCL and generate clock pulses to release the bus. The recovery is performed via a second register resource (sctrl_base) that provides direct control over SCL/SDA lines through mux and output-enable bits. After recovery, the I2C controller is reset through the ACPI _RST method and reconfigured. Recovery is only registered when the subctrl resource is provided in the firmware description, keeping backward compatibility with existing platforms. Signed-off-by: Bowen Yu --- v3 -> v4: -Some clean code fixes. --- drivers/i2c/busses/i2c-hisi.c | 146 ++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index ba5c9579ae19..051a7759bb14 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -5,6 +5,7 @@ * Copyright (c) 2021 HiSilicon Technologies Co., Ltd. */ =20 +#include #include #include #include @@ -62,6 +63,8 @@ #define HISI_I2C_INT_CLR 0x0048 #define HISI_I2C_INT_MASK 0x004C #define HISI_I2C_TRANS_STATE 0x0050 +#define HISI_I2C_TRANS_STATE_SDA_LEVEL BIT(5) +#define HISI_I2C_TRANS_STATE_SCL_LEVEL BIT(6) #define HISI_I2C_TRANS_ERR 0x0054 #define HISI_I2C_VERSION 0x0058 =20 @@ -86,9 +89,26 @@ #define NSEC_TO_CYCLES(ns, clk_rate_khz) \ DIV_ROUND_UP_ULL((clk_rate_khz) * (ns), NSEC_PER_MSEC) =20 +/* + * SUBCTRL SC_I2C_CTRL register + * Set HISI_I2C_CTRL_DAT_CFG_EN and HISI_I2C_CTRL_SCL_CFG_EN to control + * I2C pin behavior by subctrl controller; use HISI_I2C_CTRL_DAT_OE and + * HISI_I2C_CTRL_CLK_OE to control input or output; use HISI_I2C_CTRL_SDA_= OUT + * and HISI_I2C_CTRL_SCL_OUT to control output value. + */ +#define HISI_I2C_CTRL_DAT_CFG_EN BIT(5) +#define HISI_I2C_CTRL_SCL_CFG_EN BIT(4) +#define HISI_I2C_CTRL_DAT_OE BIT(3) +#define HISI_I2C_CTRL_CLK_OE BIT(2) +#define HISI_I2C_CTRL_SDA_OUT BIT(1) +#define HISI_I2C_CTRL_SCL_OUT BIT(0) + +#define HISI_I2C_RECOVERY_REG_SIZE 4 + struct hisi_i2c_controller { struct i2c_adapter adapter; void __iomem *iobase; + void __iomem *sctrl_addr; struct device *dev; struct clk *clk; int irq; @@ -108,8 +128,14 @@ struct hisi_i2c_controller { struct i2c_timings t; u32 clk_rate_khz; u32 spk_len; + + /* Bus recovery */ + struct i2c_bus_recovery_info rinfo; + acpi_handle acpi_handle; }; =20 +static void hisi_i2c_configure_bus(struct hisi_i2c_controller *ctlr); + static void hisi_i2c_enable_int(struct hisi_i2c_controller *ctlr, u32 mask) { writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_MASK); @@ -151,6 +177,122 @@ static void hisi_i2c_handle_errors(struct hisi_i2c_co= ntroller *ctlr) } } =20 +#ifdef CONFIG_ACPI +static int hisi_i2c_recovery_get_scl(struct i2c_adapter *adap) +{ + struct hisi_i2c_controller *ctlr =3D i2c_get_adapdata(adap); + u32 reg =3D readl(ctlr->iobase + HISI_I2C_TRANS_STATE); + + return !!(reg & HISI_I2C_TRANS_STATE_SCL_LEVEL); +} + +static int hisi_i2c_recovery_get_sda(struct i2c_adapter *adap) +{ + struct hisi_i2c_controller *ctlr =3D i2c_get_adapdata(adap); + u32 reg =3D readl(ctlr->iobase + HISI_I2C_TRANS_STATE); + + return !!(reg & HISI_I2C_TRANS_STATE_SDA_LEVEL); +} + +static void hisi_i2c_recovery_set_scl(struct i2c_adapter *adap, int val) +{ + struct hisi_i2c_controller *ctlr =3D i2c_get_adapdata(adap); + u32 reg; + + reg =3D readl(ctlr->sctrl_addr); + if (val) + reg |=3D HISI_I2C_CTRL_SCL_OUT; + else + reg &=3D ~HISI_I2C_CTRL_SCL_OUT; + writel(reg, ctlr->sctrl_addr); +} + +static void hisi_i2c_prepare_recovery(struct i2c_adapter *adap) +{ + struct hisi_i2c_controller *ctlr =3D i2c_get_adapdata(adap); + u32 reg; + + reg =3D readl(ctlr->sctrl_addr); + reg |=3D HISI_I2C_CTRL_SCL_CFG_EN | HISI_I2C_CTRL_DAT_CFG_EN | + HISI_I2C_CTRL_CLK_OE | HISI_I2C_CTRL_SCL_OUT; + reg &=3D ~HISI_I2C_CTRL_DAT_OE; + writel(reg, ctlr->sctrl_addr); +} + +static void hisi_i2c_unprepare_recovery(struct i2c_adapter *adap) +{ + struct hisi_i2c_controller *ctlr =3D i2c_get_adapdata(adap); + u32 reg; + + reg =3D readl(ctlr->sctrl_addr); + reg &=3D ~(HISI_I2C_CTRL_SCL_CFG_EN | HISI_I2C_CTRL_DAT_CFG_EN); + writel(reg, ctlr->sctrl_addr); + + /* + * Invokes the specific ACPI method "_RST" to trigger a soft reset + * of the I2C controller to help the I2C controller recover from + * the abnormal state after the bus recovery process. + */ + if (ctlr->acpi_handle && acpi_has_method(ctlr->acpi_handle, "_RST")) { + acpi_status status; + + status =3D acpi_evaluate_object(ctlr->acpi_handle, "_RST", NULL, NULL); + if (ACPI_FAILURE(status)) + dev_err(ctlr->dev, "_RST method failed: %s\n", + acpi_format_exception(status)); + } + hisi_i2c_configure_bus(ctlr); +} + +static int hisi_i2c_get_bus_recovery_res(struct hisi_i2c_controller *ctlr, + struct platform_device *pdev) +{ + struct resource *res0; + + res0 =3D platform_get_resource(pdev, IORESOURCE_MEM, 1); + + if (!res0 || resource_size(res0) !=3D HISI_I2C_RECOVERY_REG_SIZE) + return -ENODEV; + + ctlr->sctrl_addr =3D devm_ioremap_resource(&pdev->dev, res0); + if (IS_ERR(ctlr->sctrl_addr)) { + ctlr->sctrl_addr =3D NULL; + return -ENOMEM; + } + + return 0; +} + +static int hisi_i2c_recovery_init(struct hisi_i2c_controller *ctlr) +{ + struct platform_device *pdev =3D to_platform_device(ctlr->dev); + struct i2c_adapter *adapter =3D &ctlr->adapter; + int ret; + + if (acpi_disabled) + return -ENODEV; + + ret =3D hisi_i2c_get_bus_recovery_res(ctlr, pdev); + if (ret) + return ret; + + ctlr->rinfo =3D (struct i2c_bus_recovery_info) { + .get_scl =3D hisi_i2c_recovery_get_scl, + .get_sda =3D hisi_i2c_recovery_get_sda, + .set_scl =3D hisi_i2c_recovery_set_scl, + .prepare_recovery =3D hisi_i2c_prepare_recovery, + .unprepare_recovery =3D hisi_i2c_unprepare_recovery, + .recover_bus =3D i2c_generic_scl_recovery, + }; + adapter->bus_recovery_info =3D &ctlr->rinfo; + ctlr->acpi_handle =3D ACPI_HANDLE(ctlr->dev); + + return 0; +} +#else +static int hisi_i2c_recovery_init(struct hisi_i2c_controller *ctlr) { retu= rn -ENODEV; } +#endif /* CONFIG_ACPI */ + static int hisi_i2c_start_xfer(struct hisi_i2c_controller *ctlr) { struct i2c_msg *msg =3D ctlr->msgs; @@ -496,6 +638,10 @@ static int hisi_i2c_probe(struct platform_device *pdev) =20 hisi_i2c_configure_bus(ctlr); =20 + ret =3D hisi_i2c_recovery_init(ctlr); + if (ret) + dev_info(ctlr->dev, "I2C bus recovery not available\n"); + adapter =3D &ctlr->adapter; snprintf(adapter->name, sizeof(adapter->name), "HiSilicon I2C Controller %s", dev_name(dev)); --=20 2.33.0