Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2231227/?format=api
{ "id": 2231227, "url": "http://patchwork.ozlabs.org/api/patches/2231227/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260430121354.6253-2-zain_zhou@realsil.com.cn/", "project": { "id": 42, "url": "http://patchwork.ozlabs.org/api/projects/42/?format=api", "name": "Linux GPIO development", "link_name": "linux-gpio", "list_id": "linux-gpio.vger.kernel.org", "list_email": "linux-gpio@vger.kernel.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260430121354.6253-2-zain_zhou@realsil.com.cn>", "list_archive_url": null, "date": "2026-04-30T12:13:54", "name": "[2/2] staging: i3c: add Realtek RTS490x I3C HUB driver", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "057b18589dd11f593b302b11c2e8a49e3918906b", "submitter": { "id": 93291, "url": "http://patchwork.ozlabs.org/api/people/93291/?format=api", "name": "", "email": "zain_zhou@realsil.com.cn" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260430121354.6253-2-zain_zhou@realsil.com.cn/mbox/", "series": [ { "id": 502291, "url": "http://patchwork.ozlabs.org/api/series/502291/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/list/?series=502291", "date": "2026-04-30T12:13:54", "name": "[1/2] dt-bindings: i3c: add binding for Realtek RTS490x I3C HUB", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/502291/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2231227/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2231227/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-gpio+bounces-35914-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-gpio@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=realsil.com.cn header.i=@realsil.com.cn\n header.a=rsa-sha256 header.s=dkim header.b=a2kxmdAW;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c09:e001:a7::12fc:5321; helo=sto.lore.kernel.org;\n envelope-from=linux-gpio+bounces-35914-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=realsil.com.cn header.i=@realsil.com.cn\n header.b=\"a2kxmdAW\"", "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=211.75.126.72", "smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=realsil.com.cn", "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=realsil.com.cn" ], "Received": [ "from sto.lore.kernel.org (sto.lore.kernel.org\n [IPv6:2600:3c09:e001:a7::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g5tRf0Gq9z1yHZ\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 30 Apr 2026 22:15:41 +1000 (AEST)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sto.lore.kernel.org (Postfix) with ESMTP id 585173005995\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 30 Apr 2026 12:15:34 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 5EA383B38A6;\n\tThu, 30 Apr 2026 12:15:33 +0000 (UTC)", "from rtits2.realtek.com.tw (rtits2.realtek.com [211.75.126.72])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id DA97D402B89;\n\tThu, 30 Apr 2026 12:15:23 +0000 (UTC)", "from RS-EX-MBS2.realsil.com.cn ([172.29.17.102])\n\tby rtits2.realtek.com.tw (8.15.2/3.27/5.94) with ESMTPS id 63UCECynC1958474\n\t(version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL);\n\tThu, 30 Apr 2026 20:14:12 +0800", "from A106071510.realsil.com.cn (172.29.42.211) by\n RS-EX-MBS2.realsil.com.cn (172.29.17.102) with Microsoft SMTP Server\n (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n 15.2.2562.17; Thu, 30 Apr 2026 20:14:12 +0800" ], "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1777551333; cv=none;\n b=TzKLXpmEiQzRZ7Aditw5CbFCehzPFDtRV420If9t4fgQb02VJAAMA/8i9UV8X/UzRc51cAIRx+ZPOA09A23cGnIzC1CsEUHcPKDpqTUyn6syU1MzJoUp8x4248J3iDDcE9QpMPz8BVZ89ppzmq5Fsy7KQZev8GWa+TqTaHgQOdM=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1777551333; c=relaxed/simple;\n\tbh=P2gVdNDkfLi4SOK5cL0BTxFdzJBCdZN4ojU3BBon23c=;\n\th=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version:Content-Type;\n b=e4SXQkncSxU0KkryLwi7YI+9oO9x+xDX5fQA4wJuYIZyMExYw0uhSGniUHd97CYlXFHfpSbu9eYjX1Y8w1SfCILqbAfOAhAoTFgGmDDbGZ+zC4KrCkeQlUx7neXPy5sMCYyZCdGYE5t1s6Imiz1rfYspO/0xnPZJ4fYDll74uWo=", "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=realsil.com.cn;\n spf=pass smtp.mailfrom=realsil.com.cn;\n dkim=pass (2048-bit key) header.d=realsil.com.cn header.i=@realsil.com.cn\n header.b=a2kxmdAW; arc=none smtp.client-ip=211.75.126.72", "X-SpamFilter-By": "ArmorX SpamTrap 5.80 with qID 63UCECynC1958474,\n This message is accepted by code: ctloc85258", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=realsil.com.cn;\n\ts=dkim; t=1777551252;\n\tbh=0ZCtGdf1Ds32E2PScHOp4UDJAjhQuS3TYtOMKjoZf3E=;\n\th=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version:Content-Transfer-Encoding:Content-Type;\n\tb=a2kxmdAW1j57jbOXaLrRSeTu/IphSn8pvEseHyWv7veUGQ56ytELv4t+n8EoJp1Is\n\t V+lEVy/bONYA1u1RxSqQyVy/NKhyIA8+FkMOQepMWwnB/K6nWINcthYSfyF4ZCZvp6\n\t x2cxoz5zaDyoPfAKJhGNAcgLQuezkhnGXE34LAmCSfhwO0RMs5+Jjyi3E9YUSHNkxp\n\t 7YZNjuM5zVlVdG9/930AAA++9kX6I9bWc/VIGTZX0VSZ4FkeuFLFXzqYBRcSjPj1Ca\n\t HGpoWUU0oo0UE3x/MIHOCLr/mFSYF21WFWek24jOe5aROQFim0UbM8PZXOCiCENr8s\n\t bwi73v4fqPerw==", "From": "<zain_zhou@realsil.com.cn>", "To": "<linux-staging@lists.linux.dev>, <linux-i3c@lists.infradead.org>,\n <devicetree@vger.kernel.org>", "CC": "<gregkh@linuxfoundation.org>, <alexandre.belloni@bootlin.com>,\n <Frank.Li@nxp.com>, <robh@kernel.org>, <krzk+dt@kernel.org>,\n <conor+dt@kernel.org>, <linusw@kernel.org>, <brgl@kernel.org>,\n <linux-gpio@vger.kernel.org>, <linux-kernel@vger.kernel.org>,\n zain_zhou\n\t<zain_zhou@realsil.com.cn>", "Subject": "[PATCH 2/2] staging: i3c: add Realtek RTS490x I3C HUB driver", "Date": "Thu, 30 Apr 2026 20:13:54 +0800", "Message-ID": "<20260430121354.6253-2-zain_zhou@realsil.com.cn>", "X-Mailer": "git-send-email 2.25.1", "In-Reply-To": "<20260430121354.6253-1-zain_zhou@realsil.com.cn>", "References": "<20260430121354.6253-1-zain_zhou@realsil.com.cn>", "Precedence": "bulk", "X-Mailing-List": "linux-gpio@vger.kernel.org", "List-Id": "<linux-gpio.vger.kernel.org>", "List-Subscribe": "<mailto:linux-gpio+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-gpio+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Content-Type": "text/plain", "X-ClientProxiedBy": "RS-EX-MBS1.realsil.com.cn (172.29.17.101) To\n RS-EX-MBS2.realsil.com.cn (172.29.17.102)" }, "content": "From: zain_zhou <zain_zhou@realsil.com.cn>\n\nAdd driver for Realtek RTS490x series I3C HUB devices (RTS4900,\nRTS4901, RTS4902, RTS4903, RTS4904, RTS4906).\n\nThe I3C HUB is a smart device that provides:\n - voltage compatibility across I3C Controller and Target devices\n - bus capacitance isolation\n - address conflict isolation\n - I3C port expansion (up to 8 target ports)\n - dual controller port support\n - I3C and SMBus device compatibility\n - GPIO expansion via target ports\n\nThe driver supports:\n - Device Tree based configuration of LDO, pull-up, IO strength\n and per-port mode (I3C/SMBus/GPIO/disabled)\n - Logical I3C bus registration per target port\n - SMBus agent functionality with IBI and polling modes\n - GPIO chip with IRQ support\n - DebugFS interface for register access and DT config inspection\n - IBI (In-Band Interrupt) handling\n\nThe driver is placed in staging as it has known issues to be resolved\nbefore mainlining; see drivers/staging/rts490x/TODO for details.\n\nSigned-off-by: zain_zhou <zain_zhou@realsil.com.cn>\n---\n drivers/staging/Kconfig | 2 +\n drivers/staging/Makefile | 1 +\n drivers/staging/rts490x/Kconfig | 16 +\n drivers/staging/rts490x/Makefile | 2 +\n drivers/staging/rts490x/TODO | 19 +\n drivers/staging/rts490x/rts490xa-i3c-hub.c | 3162 ++++++++++++++++++++\n 6 files changed, 3202 insertions(+)\n create mode 100644 drivers/staging/rts490x/Kconfig\n create mode 100644 drivers/staging/rts490x/Makefile\n create mode 100644 drivers/staging/rts490x/TODO\n create mode 100644 drivers/staging/rts490x/rts490xa-i3c-hub.c", "diff": "diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig\nindex 2f92cd698bef..f14869cf5af5 100644\n--- a/drivers/staging/Kconfig\n+++ b/drivers/staging/Kconfig\n@@ -48,4 +48,6 @@ source \"drivers/staging/axis-fifo/Kconfig\"\n \n source \"drivers/staging/vme_user/Kconfig\"\n \n+source \"drivers/staging/rts490x/Kconfig\"\n+\n endif # STAGING\ndiff --git a/drivers/staging/Makefile b/drivers/staging/Makefile\nindex f5b8876aa536..59044d547bf7 100644\n--- a/drivers/staging/Makefile\n+++ b/drivers/staging/Makefile\n@@ -13,3 +13,4 @@ obj-$(CONFIG_MOST)\t\t+= most/\n obj-$(CONFIG_GREYBUS)\t\t+= greybus/\n obj-$(CONFIG_BCM2835_VCHIQ)\t+= vc04_services/\n obj-$(CONFIG_XIL_AXIS_FIFO)\t+= axis-fifo/\n+obj-$(CONFIG_RTS490X_I3C_HUB)\t+= rts490x/\ndiff --git a/drivers/staging/rts490x/Kconfig b/drivers/staging/rts490x/Kconfig\nnew file mode 100644\nindex 000000000000..282865d1fed9\n--- /dev/null\n+++ b/drivers/staging/rts490x/Kconfig\n@@ -0,0 +1,16 @@\n+# SPDX-License-Identifier: GPL-2.0-only\n+config RTS490X_I3C_HUB\n+\ttristate \"Realtek RTS490x I3C HUB driver\"\n+\tdepends on I3C\n+\tdepends on REGMAP_I3C\n+\tselect GPIOLIB\n+\thelp\n+\t Support for Realtek RTS490x series I3C HUB devices (RTS4900,\n+\t RTS4901, RTS4902, RTS4903, RTS4904, RTS4906).\n+\n+\t The I3C HUB provides port expansion, voltage level translation,\n+\t bus capacitance isolation, address conflict isolation, SMBus\n+\t agent functionality and GPIO expansion.\n+\n+\t This driver can also be built as a module. If so, the module\n+\t will be called rts490xa-i3c-hub.\ndiff --git a/drivers/staging/rts490x/Makefile b/drivers/staging/rts490x/Makefile\nnew file mode 100644\nindex 000000000000..4b1d7640fb82\n--- /dev/null\n+++ b/drivers/staging/rts490x/Makefile\n@@ -0,0 +1,2 @@\n+# SPDX-License-Identifier: GPL-2.0-only\n+obj-$(CONFIG_RTS490X_I3C_HUB)\t+= rts490xa-i3c-hub.o\ndiff --git a/drivers/staging/rts490x/TODO b/drivers/staging/rts490x/TODO\nnew file mode 100644\nindex 000000000000..0be2d7693d68\n--- /dev/null\n+++ b/drivers/staging/rts490x/TODO\n@@ -0,0 +1,19 @@\n+TODO list for rts490xa-i3c-hub staging driver\n+==============================================\n+\n+- Move driver out of staging once the following are addressed:\n+ - Add proper DT binding schema validation (dt-schema)\n+ - Clean up open-coded OF property parsing; use device_property_* APIs\n+ instead of of_property_read_* where possible\n+ - Remove use of full_name / sscanf for node name parsing; use\n+ of_node_name_eq() and fwnode helpers instead\n+ - Replace global mutex (i3c_hub_regmap_mutex) with per-device locking\n+ - Add kernel-doc comments for all exported/public functions\n+ - Resolve TODO comment in i3c_hub_hw_configure_tp() regarding MUX\n+ connection verification\n+ - Remove TBD comment in i3c_hub_probe() regarding DEV_CMD security lock\n+ - Review and fix potential locking issues in i3c_hub_delayed_work()\n+ when registering logical buses\n+ - Fix error handling in i3c_hub_delayed_work(): early return on failure\n+ does not unregister already-registered logical buses, causing resource\n+ leak; needs proper cleanup on error path\ndiff --git a/drivers/staging/rts490x/rts490xa-i3c-hub.c b/drivers/staging/rts490x/rts490xa-i3c-hub.c\nnew file mode 100644\nindex 000000000000..b557cfe89013\n--- /dev/null\n+++ b/drivers/staging/rts490x/rts490xa-i3c-hub.c\n@@ -0,0 +1,3162 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (C) 2021 - 2023 Intel Corporation. */\n+/* Copyright (c) 2025 Realtek Semiconductor Corp. */\n+\n+#include <linux/bits.h>\n+#include <linux/kernel.h>\n+#include <linux/ktime.h>\n+#include <linux/bitfield.h>\n+#include <linux/debugfs.h>\n+#include <linux/module.h>\n+#include <linux/property.h>\n+#include <linux/regmap.h>\n+#include <linux/list.h>\n+#include <linux/gpio/driver.h>\n+\n+#include <linux/i3c/device.h>\n+#include <linux/i3c/master.h>\n+\n+#define I3C_HUB_TP_MAX_COUNT 0x08\n+\n+#define I3C_DCR_HUB 0xC2\n+\n+#define GPIO_BANK_SZ 0x02\n+#define GPIO_MAX_BANK I3C_HUB_TP_MAX_COUNT\n+\n+/* I3C HUB REGISTERS */\n+\n+/*\n+ * In this driver Controller - Target convention is used. All the abbreviations are\n+ * based on this convention. For instance: CP - Controller Port, TP - Target Port.\n+ */\n+\n+/* Device Information Registers */\n+#define I3C_HUB_DEV_INFO_0 0x00\n+#define I3C_HUB_DEV_INFO_1 0x01\n+#define I3C_HUB_PID_5\t 0x02\n+#define I3C_HUB_PID_4\t 0x03\n+#define I3C_HUB_PID_3\t 0x04\n+#define I3C_HUB_PID_2\t 0x05\n+#define I3C_HUB_PID_1\t 0x06\n+#define I3C_HUB_PID_0\t 0x07\n+#define I3C_HUB_BCR\t 0x08\n+#define I3C_HUB_DCR\t 0x09\n+#define I3C_HUB_DEV_CAPAB 0x0A\n+\n+#define I3C_HUB_DEV_REV\t 0x0B\n+#define I3C_HUB_DEV_REV_LDO_MASK GENMASK(7, 6)\n+#define I3C_HUB_DEV_REV_LDO_GET(x) FIELD_GET(I3C_HUB_DEV_REV_LDO_MASK, (x))\n+\n+/* Device Configuration Registers */\n+#define I3C_HUB_PROTECTION_CODE\t 0x10\n+#define REGISTERS_LOCK_CODE\t 0x00\n+#define REGISTERS_UNLOCK_CODE\t 0x69\n+#define CP1_REGISTERS_UNLOCK_CODE 0x6A\n+\n+#define I3C_HUB_CP_CONF\t 0x11\n+#define I3C_HUB_TP_ENABLE 0x12\n+#define TPn_ENABLE(n)\t BIT(n)\n+\n+#define I3C_HUB_DEV_CONF\t0x13\n+#define I3C_HUB_IO_STRENGTH\t0x14\n+#define TP0145_IO_STRENGTH_MASK GENMASK(1, 0)\n+#define TP0145_IO_STRENGTH(x)\t(((x) << 0) & TP0145_IO_STRENGTH_MASK)\n+#define TP2367_IO_STRENGTH_MASK GENMASK(3, 2)\n+#define TP2367_IO_STRENGTH(x)\t(((x) << 2) & TP2367_IO_STRENGTH_MASK)\n+#define CP0_IO_STRENGTH_MASK\tGENMASK(5, 4)\n+#define CP0_IO_STRENGTH(x)\t(((x) << 4) & CP0_IO_STRENGTH_MASK)\n+#define CP1_IO_STRENGTH_MASK\tGENMASK(7, 6)\n+#define CP1_IO_STRENGTH(x)\t(((x) << 6) & CP1_IO_STRENGTH_MASK)\n+#define IO_STRENGTH_20_OHM\t0x00\n+#define IO_STRENGTH_30_OHM\t0x01\n+#define IO_STRENGTH_40_OHM\t0x02\n+#define IO_STRENGTH_50_OHM\t0x03\n+\n+#define I3C_HUB_NET_OPER_MODE_CONF 0x15\n+#define I3C_HUB_NET_ALWAYS_I3C_EN BIT(5)\n+\n+#define I3C_HUB_LDO_CONF\t 0x16\n+#define CP0_LDO_VOLTAGE_MASK\t GENMASK(1, 0)\n+#define CP0_LDO_VOLTAGE(x)\t (((x) << 0) & CP0_LDO_VOLTAGE_MASK)\n+#define CP1_LDO_VOLTAGE_MASK\t GENMASK(3, 2)\n+#define CP1_LDO_VOLTAGE(x)\t (((x) << 2) & CP1_LDO_VOLTAGE_MASK)\n+#define TP0145_LDO_VOLTAGE_MASK\t GENMASK(5, 4)\n+#define TP0145_LDO_VOLTAGE(x)\t (((x) << 4) & TP0145_LDO_VOLTAGE_MASK)\n+#define TP2367_LDO_VOLTAGE_MASK\t GENMASK(7, 6)\n+#define TP2367_LDO_VOLTAGE(x)\t (((x) << 6) & TP2367_LDO_VOLTAGE_MASK)\n+#define LDO_VOLTAGE_1_0V\t 0x00\n+#define LDO_VOLTAGE_1_1V\t 0x01\n+#define LDO_VOLTAGE_1_2V\t 0x02\n+#define LDO_VOLTAGE_1_8V\t 0x03\n+\n+#define I3C_HUB_TP_IO_MODE_CONF\t 0x17\n+#define TPn_IO_MODE_CON(n)\t BIT(n)\n+#define I3C_HUB_TP_SMBUS_AGNT_EN 0x18\n+#define TPn_SMBUS_MODE_EN(n)\t BIT(n)\n+\n+#define I3C_HUB_LDO_AND_PULLUP_CONF 0x19\n+#define LDO_ENABLE_DISABLE_MASK\t GENMASK(3, 0)\n+#define CP0_LDO_EN\t\t BIT(0)\n+#define CP1_LDO_EN\t\t BIT(1)\n+/*\n+ * I3C HUB does not provide a way to control LDO or pull-up for individual ports. It is possible\n+ * for group of ports TP0/TP1/TP4/TP5 and TP2/TP3/TP6/TP7.\n+ */\n+#define TP0145_LDO_EN\t\t BIT(2)\n+#define TP2367_LDO_EN\t\t BIT(3)\n+#define TP0145_PULLUP_CONF_MASK\t GENMASK(7, 6)\n+#define TP0145_PULLUP_CONF(x)\t (((x) << 6) & TP0145_PULLUP_CONF_MASK)\n+#define TP2367_PULLUP_CONF_MASK\t GENMASK(5, 4)\n+#define TP2367_PULLUP_CONF(x)\t (((x) << 4) & TP2367_PULLUP_CONF_MASK)\n+#define PULLUP_250R\t\t 0x00\n+#define PULLUP_500R\t\t 0x01\n+#define PULLUP_1K\t\t 0x02\n+#define PULLUP_2K\t\t 0x03\n+\n+#define I3C_HUB_CP_IBI_CONF\t 0x1A\n+#define I3C_HUB_TP_IBI_CONF\t 0x1B\n+#define I3C_HUB_IBI_MDB_CUSTOM\t 0x1C\n+#define I3C_HUB_JEDEC_CONTEXT_ID 0x1D\n+#define I3C_HUB_TP_GPIO_MODE_EN\t 0x1E\n+#define TPn_GPIO_MODE_EN(n)\t BIT(n)\n+\n+/* Device Status and IBI Registers */\n+#define I3C_HUB_DEV_AND_IBI_STS\t 0x20\n+#define PEC_ERROR_FLAG\t\t BIT(0)\n+#define PARITY_ERROR_FLAG\t BIT(1)\n+#define CONTROLLER_MSG_PENDING_FLAG BIT(2)\n+#define TP_IO_FLAG_STATUS\t BIT(3)\n+#define SMBUS_AGENT_EVENT_FLAG_STATUS BIT(4)\n+\n+#define I3C_HUB_TP_SMBUS_AGNT_IBI_STS 0x21\n+\n+/* Controller Port Control/Status Registers */\n+#define I3C_HUB_CP_MUX_SET\t\t 0x38\n+#define CONTROLLER_PORT_MUX_REQ\t\t BIT(0)\n+#define I3C_HUB_CP_MUX_STS\t\t 0x39\n+#define CONTROLLER_PORT_MUX_CONNECTION_STATUS BIT(0)\n+\n+/* Target Dynamic Address Assignment Flag Registers */\n+#define I3C_HUB_TARGET_DA_FLAG_BYTE_BASE 0x40\n+#define I3C_HUB_TARGET_DA_FLAG_BYTE_COUNT 16\n+\n+/* Target Ports Control Registers */\n+#define I3C_HUB_TP_SMBUS_AGNT_TRANS_START 0x50\n+#define I3C_HUB_TP_NET_CON_CONF\t\t 0x51\n+#define TPn_NET_CON(n)\t\t\t BIT(n)\n+\n+#define I3C_HUB_TP_PULLUP_EN 0x53\n+#define TPn_PULLUP_EN(n) BIT(n)\n+\n+#define I3C_HUB_TP_SCL_OUT_EN\t 0x54\n+#define I3C_HUB_TP_SDA_OUT_EN\t 0x55\n+#define I3C_HUB_TP_SCL_OUT_LEVEL 0x56\n+#define I3C_HUB_TP_SDA_OUT_LEVEL 0x57\n+\n+#define I3C_HUB_TP_IN_DETECT_MODE_CONF 0x58\n+#define SCL0145_IO_IN_DET_CFG_MASK GENMASK(1, 0)\n+#define SCL0145_IO_IN_DET_CFG(x) (((x) << 0) & SCL0145_IO_IN_DET_CFG_MASK)\n+#define SDA0145_IO_IN_DET_CFG_MASK GENMASK(3, 2)\n+#define SDA0145_IO_IN_DET_CFG(x) (((x) << 2) & SDA0145_IO_IN_DET_CFG_MASK)\n+#define SCL2367_IO_IN_DET_CFG_MASK GENMASK(5, 4)\n+#define SCL2367_IO_IN_DET_CFG(x) (((x) << 4) & SCL2367_IO_IN_DET_CFG_MASK)\n+#define SDA2367_IO_IN_DET_CFG_MASK GENMASK(7, 6)\n+#define SDA2367_IO_IN_DET_CFG(x) (((x) << 6) & SDA2367_IO_IN_DET_CFG_MASK)\n+\n+#define I3C_HUB_TP_SCL_IN_DETECT_IBI_EN 0x59\n+#define I3C_HUB_TP_SDA_IN_DETECT_IBI_EN 0x5A\n+\n+/* Target Ports Status Registers */\n+#define I3C_HUB_TP_SCL_IN_LEVEL_STS 0x60\n+#define I3C_HUB_TP_SDA_IN_LEVEL_STS 0x61\n+#define I3C_HUB_TP_SCL_IN_DETECT_FLG 0x62\n+#define I3C_HUB_TP_SDA_IN_DETECT_FLG 0x63\n+\n+/* SMBus Agent Configuration and Status Registers */\n+#define I3C_HUB_TP0_SMBUS_AGNT_STS\t 0x64\n+#define I3C_HUB_TP1_SMBUS_AGNT_STS\t 0x65\n+#define I3C_HUB_TP2_SMBUS_AGNT_STS\t 0x66\n+#define I3C_HUB_TP3_SMBUS_AGNT_STS\t 0x67\n+#define I3C_HUB_TP4_SMBUS_AGNT_STS\t 0x68\n+#define I3C_HUB_TP5_SMBUS_AGNT_STS\t 0x69\n+#define I3C_HUB_TP6_SMBUS_AGNT_STS\t 0x6A\n+#define I3C_HUB_TP7_SMBUS_AGNT_STS\t 0x6B\n+\n+#define I3C_HUB_ONCHIP_TD_AND_SMBUS_AGNT_CONF 0x6C\n+#define TARGET_AGENT_BUF_FULL_SDA_LOW_EN BIT(5)\n+\n+/* Transaction status checking mask */\n+#define I3C_HUB_CONTROLLER_AGENT_STATUS_MASK\t\t (0xF0 | BIT(0))\n+#define I3C_HUB_CONTROLLER_AGENT_FINISH_FLAG\t\t BIT(0)\n+/* SMBus Controller Agent Return Codes */\n+#define I3C_HUB_CONTROLLER_AGENT_RET_CODE_SHIFT\t\t 4\n+#define I3C_HUB_CONTROLLER_AGENT_RET_CODE_SUCCESS\t 0x0\n+#define I3C_HUB_CONTROLLER_AGENT_RET_CODE_ADDRESS_NACK\t 0x1\n+#define I3C_HUB_CONTROLLER_AGENT_RET_CODE_DEVICE_BUSY\t 0x2\n+#define I3C_HUB_CONTROLLER_AGENT_RET_CODE_READ_NOT_READY 0x3\n+#define I3C_HUB_CONTROLLER_AGENT_RET_CODE_SYNC_RECOVERED 0x4\n+#define I3C_HUB_CONTROLLER_AGENT_RET_CODE_SYNC_BUS_CLEAR 0x5\n+#define I3C_HUB_CONTROLLER_AGENT_RET_CODE_BUS_FAULT\t 0x6\n+#define I3C_HUB_CONTROLLER_AGENT_RET_CODE_ARBITRATION_LOST 0x7\n+#define I3C_HUB_CONTROLLER_AGENT_RET_CODE_SCL_TIMEOUT\t 0x8\n+\n+#define I3C_HUB_TARGET_BUF_STATUS_MASK GENMASK(3, 1)\n+#define I3C_HUB_TARGET_BUF_0_RECEIVE BIT(1)\n+#define I3C_HUB_TARGET_BUF_1_RECEIVE BIT(2)\n+#define I3C_HUB_TARGET_BUF_OVRFL BIT(3)\n+\n+/* Special Function Registers */\n+#define I3C_HUB_LDO_AND_CPSEL_STS 0x79\n+#define CP_SDA1_LEVEL\t\t BIT(7)\n+#define CP_SCL1_LEVEL\t\t BIT(6)\n+#define CP_SEL_PIN_INPUT_CODE_MASK GENMASK(5, 4)\n+#define CP_SEL_PIN_INPUT_CODE_GET(x) (((x) & CP_SEL_PIN_INPUT_CODE_MASK) >> 4)\n+#define CP_SDA1_SCL1_PINS_CODE_MASK GENMASK(7, 6)\n+#define CP_SDA1_SCL1_PINS_CODE_GET(x) (((x) & CP_SDA1_SCL1_PINS_CODE_MASK) >> 6)\n+#define VCCIO1_PWR_GOOD\t\t BIT(3)\n+#define VCCIO0_PWR_GOOD\t\t BIT(2)\n+#define CP1_VCCIO_PWR_GOOD\t BIT(1)\n+#define CP0_VCCIO_PWR_GOOD\t BIT(0)\n+\n+#define I3C_HUB_BUS_RESET_SCL_TIMEOUT\t0x7A\n+#define I3C_HUB_ONCHIP_TD_PROTO_ERR_FLG 0x7B\n+#define I3C_HUB_DEV_CMD\t\t\t0x7C\n+#define I3C_HUB_ONCHIP_TD_STS\t\t0x7D\n+#define I3C_HUB_ONCHIP_TD_ADDR_CONF\t0x7E\n+#define I3C_HUB_PAGE_PTR\t\t0x7F\n+\n+/* LDO Disable/Enable DT settings */\n+#define I3C_HUB_DT_LDO_DISABLED\t 0x00\n+#define I3C_HUB_DT_LDO_ENABLED\t 0x01\n+#define I3C_HUB_DT_LDO_NOT_DEFINED 0xFF\n+\n+/* LDO Voltage DT settings */\n+#define I3C_HUB_DT_LDO_VOLT_1_0V\t0x00\n+#define I3C_HUB_DT_LDO_VOLT_1_1V\t0x01\n+#define I3C_HUB_DT_LDO_VOLT_1_2V\t0x02\n+#define I3C_HUB_DT_LDO_VOLT_1_8V\t0x03\n+#define I3C_HUB_DT_LDO_VOLT_NOT_DEFINED 0xFF\n+\n+/* Paged Transaction Registers */\n+#define I3C_HUB_CONTROLLER_BUFFER_PAGE\t 0x10\n+#define I3C_HUB_CONTROLLER_AGENT_BUFF\t 0x80\n+#define I3C_HUB_CONTROLLER_AGENT_BUFF_DATA 0x84\n+#define I3C_HUB_TARGET_BUFF_LENGTH\t 0x80\n+#define I3C_HUB_TARGET_BUFF_ADDRESS\t 0x81\n+#define I3C_HUB_TARGET_BUFF_DATA\t 0x82\n+\n+/* Pull-up DT settings */\n+#define I3C_HUB_DT_PULLUP_DISABLED 0x00\n+#define I3C_HUB_DT_PULLUP_250R\t 0x01\n+#define I3C_HUB_DT_PULLUP_500R\t 0x02\n+#define I3C_HUB_DT_PULLUP_1K\t 0x03\n+#define I3C_HUB_DT_PULLUP_2K\t 0x04\n+#define I3C_HUB_DT_PULLUP_NOT_DEFINED 0xFF\n+\n+/* TP DT setting */\n+#define I3C_HUB_DT_TP_MODE_DISABLED 0x00\n+#define I3C_HUB_DT_TP_MODE_I3C\t 0x01\n+#define I3C_HUB_DT_TP_MODE_SMBUS 0x02\n+#define I3C_HUB_DT_TP_MODE_GPIO\t 0x03\n+#define I3C_HUB_DT_TP_MODE_NOT_DEFINED 0xFF\n+\n+/* TP pull-up status */\n+#define I3C_HUB_DT_TP_PULLUP_DISABLED\t 0x00\n+#define I3C_HUB_DT_TP_PULLUP_ENABLED\t 0x01\n+#define I3C_HUB_DT_TP_PULLUP_NOT_DEFINED 0xFF\n+\n+/* TP IO mode */\n+#define I3C_HUB_DT_TP_IO_MODE_OD_PP\t 0x00\n+#define I3C_HUB_DT_TP_IO_MODE_OD\t 0x01\n+#define I3C_HUB_DT_TP_IO_MODE_NOT_DEFINED 0xFF\n+\n+/* CP/TP IO strength */\n+#define I3C_HUB_DT_IO_STRENGTH_20_OHM\t 0x00\n+#define I3C_HUB_DT_IO_STRENGTH_30_OHM\t 0x01\n+#define I3C_HUB_DT_IO_STRENGTH_40_OHM\t 0x02\n+#define I3C_HUB_DT_IO_STRENGTH_50_OHM\t 0x03\n+#define I3C_HUB_DT_IO_STRENGTH_NOT_DEFINED 0xFF\n+\n+/* SMBus transaction types fields */\n+#define I3C_HUB_SMBUS_100kHz 0x00\n+#define I3C_HUB_SMBUS_200kHz BIT(1)\n+#define I3C_HUB_SMBUS_400kHz BIT(2)\n+#define I3C_HUB_SMBUS_1000kHz (BIT(1) | BIT(2))\n+\n+/* SMBus xfer type for i3c_hub_smbus_msg xfer_type parameter */\n+#define I3C_HUB_SMBUS_XFER_WRITE 0\n+#define I3C_HUB_SMBUS_XFER_READ\t 1\n+#define I3C_HUB_SMBUS_XFER_WR_RD 2\n+\n+/* Hub buffer size */\n+#define I3C_HUB_CONTROLLER_BUFFER_SIZE 88\n+#define I3C_HUB_TARGET_BUFFER_SIZE 80\n+#define I3C_HUB_SMBUS_DESCRIPTOR_SIZE 4\n+#define I3C_HUB_SMBUS_PAYLOAD_SIZE \\\n+\t(I3C_HUB_CONTROLLER_BUFFER_SIZE - I3C_HUB_SMBUS_DESCRIPTOR_SIZE)\n+#define I3C_HUB_SMBUS_TARGET_PAYLOAD_SIZE (I3C_HUB_TARGET_BUFFER_SIZE - 2)\n+\n+/* Hub SMBus status register read interval (microseconds, ceil) */\n+#define I3C_HUB_SMBUS_STATUS_READ_INTERVAL_US_CEIL(len, clk_khz) \\\n+\tDIV_ROUND_UP(1000U * 9U * (u32)(len), (u32)(clk_khz))\n+\n+/* ID Extraction */\n+#define I3C_HUB_ID_CP_SDA_SCL 0x00\n+#define I3C_HUB_ID_CP_SEL 0x01\n+\n+/* IBI */\n+#define IBI_MAX_PAYLOAD_LEN 2\n+#define IBI_SLOT_NUMS\t 6\n+\n+#define I3C_HUB_IO_CTRL_PAGE\t\t\t0x81\n+#define I3C_HUB_CFG_TP_SCL_L_ACK_CLK\t\t0xDB\n+#define I3C_HUB_CFG_TP_SCL_L_ACK_CLK_EN\t\tBIT(6)\n+#define I3C_HUB_CFG_TP_SCL_L_ACK_CLK_COUNT_MASK GENMASK(5, 0)\n+#define I3C_HUB_CFG_TP_SCL_L_ACK_CLK_COUNT_VAL\t0x18\n+\n+#define I3C_HUB_CFG_TP_SCL_H_ACK_CLK\t\t0xDC\n+#define I3C_HUB_CFG_TP_SCL_H_ACK_CLK_EN\t\tBIT(4)\n+#define I3C_HUB_CFG_TP_SCL_H_ACK_CLK_COUNT_MASK GENMASK(3, 0)\n+#define I3C_HUB_CFG_TP_SCL_H_ACK_CLK_COUNT_VAL(x) \\\n+\t((x) & I3C_HUB_CFG_TP_SCL_H_ACK_CLK_COUNT_MASK)\n+\n+#define I3C_HUB_EFUSE_PAGE\t 0x7B\n+#define I3C_HUB_EFUSE_OFFSET_A0\t 0xA0\n+#define I3C_HUB_FAST_RSON_EN\t BIT(5)\n+#define I3C_HUB_EFUSE_OFFSET_A3\t 0xA3\n+#define I3C_HUB_FAST_DRV_LOOP_DIS BIT(5)\n+\n+#define I3C_HUB_EFUSE_OFFSET_9D 0x9D\n+#define I3C_HUB_TP_OD_VOL_LEVEL BIT(0)\n+#define I3C_HUB_TP_OD_VREF\tBIT(1)\n+\n+#define I3C_HUB_EFUSE_OFFSET_9E\t\t 0x9E\n+#define I3C_HUB_FAST_DRV_H_ADD_CYCLE_MASK GENMASK(5, 4)\n+#define I3C_HUB_FAST_DRV_H_ADD_CYCLE_VAL(x) \\\n+\t(((x) << 4) & I3C_HUB_FAST_DRV_H_ADD_CYCLE_MASK)\n+#define I3C_HUB_IBI_ACK_RD_CYCLE_MASK\t GENMASK(3, 0)\n+#define I3C_HUB_IBI_ACK_RD_CYCLE_VAL\t (5)\n+\n+struct i3c_hub_dev_info {\n+\tconst char *model;\n+\tu16 part_id;\n+\tu8 n_ports;\n+};\n+\n+static const struct i3c_hub_dev_info i3c_hub_dev_info_unknown = {\n+\t.model = \"Unknown\",\n+\t.part_id = 0,\n+\t.n_ports = 8,\n+};\n+\n+static const struct i3c_hub_dev_info i3c_hub_dev_info_table[] = {\n+\t{ \"RTS4900\", 0x4000, 4 }, { \"RTS4901\", 0x4100, 4 },\n+\t{ \"RTS4902\", 0x8000, 8 }, { \"RTS4903\", 0x8100, 8 },\n+\t{ \"RTS4904\", 0x4001, 4 }, { \"RTS4906\", 0x8001, 8 }\n+};\n+\n+struct tp_setting {\n+\tu8 mode;\n+\tu8 pullup_en;\n+\tu8 io_mode;\n+\tbool always_enable;\n+\tu32 poll_interval_ms;\n+\tu32 clock_frequency;\n+};\n+\n+struct dt_settings {\n+\tu8 cp0_ldo_en;\n+\tu8 cp1_ldo_en;\n+\tu8 cp0_ldo_volt;\n+\tu8 cp1_ldo_volt;\n+\tu8 tp0145_ldo_en;\n+\tu8 tp2367_ldo_en;\n+\tu8 tp0145_ldo_volt;\n+\tu8 tp2367_ldo_volt;\n+\tu8 tp0145_pullup;\n+\tu8 tp2367_pullup;\n+\tu8 cp0_io_strength;\n+\tu8 cp1_io_strength;\n+\tu8 tp0145_io_strength;\n+\tu8 tp2367_io_strength;\n+\tstruct tp_setting tp[I3C_HUB_TP_MAX_COUNT];\n+\tbool hub_net_always_i3c;\n+\tu8 tp_scl_h_ack_cycles;\n+\tbool handshake_optimize;\n+\tu8 fast_drv_h_add_cycles;\n+\tbool fast_rson_en;\n+\tbool tp_od_vol_optimize;\n+\tbool tp_od_vref_optimize;\n+};\n+\n+struct smbus_backend {\n+\tstruct i2c_client *client;\n+\tstruct list_head list;\n+};\n+\n+struct i2c_adapter_group {\n+\tstruct i2c_adapter i2c;\n+\tu8 tp_mask;\n+\tu8 tp_port;\n+\tu8 used;\n+\tstruct device_node *of_node;\n+\tstruct i3c_hub *priv;\n+\tstruct mutex mutex;\n+\n+\tstruct delayed_work delayed_work_polling;\n+\tstruct list_head backend_entry;\n+\tu8 last_processed_buf;\n+\n+\tu8 status;\n+\tstruct completion completion;\n+};\n+\n+struct logical_bus {\n+\tbool available; /* Logical bus configuration is available in DT. */\n+\tbool registered; /* Logical bus was registered in the framework. */\n+\tu8 tp_id;\n+\tu8 tp_map;\n+\tstruct i3c_master_controller controller;\n+\tstruct device_node *of_node;\n+\tstruct i3c_hub *priv;\n+};\n+\n+struct hub_gpio {\n+\tstruct gpio_chip chip;\n+\tint tp[GPIO_MAX_BANK];\n+\ts8 port_to_index[I3C_HUB_TP_MAX_COUNT];\n+\tint nums;\n+\tstruct irq_chip irq_chip;\n+\tstruct mutex irq_mutex;\n+};\n+\n+struct i3c_hub {\n+\tstruct i3c_device *i3cdev;\n+\tstruct i3c_master_controller *driving_master;\n+\tstruct regmap *regmap;\n+\tconst struct i3c_hub_dev_info *dev_info;\n+\tstruct dt_settings settings;\n+\tstruct delayed_work delayed_work;\n+\tint hub_pin_sel_id;\n+\tint hub_pin_cp1_id;\n+\tint hub_dt_sel_id;\n+\tint hub_dt_cp1_id;\n+\n+\tstruct logical_bus logical_bus[I3C_HUB_TP_MAX_COUNT];\n+\tstruct i2c_adapter_group smbus_port_adapter[I3C_HUB_TP_MAX_COUNT];\n+\tu8 smbus_ibi_en_mask;\n+\tstruct mutex page_mutex;\n+\n+\t/* Offset for reading HUB's register. */\n+\tu8 reg_addr;\n+\tstruct dentry *debug_dir;\n+\tstruct hub_gpio gpio;\n+};\n+\n+struct hub_setting {\n+\tconst char *const name;\n+\tconst u8 value;\n+};\n+\n+static const struct hub_setting ldo_en_settings[] = {\n+\t{ \"disabled\", I3C_HUB_DT_LDO_DISABLED },\n+\t{ \"enabled\", I3C_HUB_DT_LDO_ENABLED },\n+};\n+\n+static const struct hub_setting ldo_volt_settings[] = {\n+\t{ \"1.0V\", I3C_HUB_DT_LDO_VOLT_1_0V },\n+\t{ \"1.1V\", I3C_HUB_DT_LDO_VOLT_1_1V },\n+\t{ \"1.2V\", I3C_HUB_DT_LDO_VOLT_1_2V },\n+\t{ \"1.8V\", I3C_HUB_DT_LDO_VOLT_1_8V },\n+};\n+\n+static const struct hub_setting pullup_settings[] = {\n+\t{ \"disabled\", I3C_HUB_DT_PULLUP_DISABLED },\n+\t{ \"250R\", I3C_HUB_DT_PULLUP_250R },\n+\t{ \"500R\", I3C_HUB_DT_PULLUP_500R },\n+\t{ \"1k\", I3C_HUB_DT_PULLUP_1K },\n+\t{ \"2k\", I3C_HUB_DT_PULLUP_2K },\n+};\n+\n+static const struct hub_setting tp_mode_settings[] = {\n+\t{ \"disabled\", I3C_HUB_DT_TP_MODE_DISABLED },\n+\t{ \"i3c\", I3C_HUB_DT_TP_MODE_I3C },\n+\t{ \"smbus\", I3C_HUB_DT_TP_MODE_SMBUS },\n+\t{ \"gpio\", I3C_HUB_DT_TP_MODE_GPIO },\n+};\n+\n+static const struct hub_setting tp_pullup_settings[] = {\n+\t{ \"disabled\", I3C_HUB_DT_TP_PULLUP_DISABLED },\n+\t{ \"enabled\", I3C_HUB_DT_TP_PULLUP_ENABLED },\n+};\n+\n+static const struct hub_setting tp_io_mode_settings[] = {\n+\t{ \"od-pp\", I3C_HUB_DT_TP_IO_MODE_OD_PP },\n+\t{ \"od\", I3C_HUB_DT_TP_IO_MODE_OD },\n+};\n+\n+static const struct hub_setting io_strength_settings[] = {\n+\t{ \"20Ohms\", I3C_HUB_DT_IO_STRENGTH_20_OHM },\n+\t{ \"30Ohms\", I3C_HUB_DT_IO_STRENGTH_30_OHM },\n+\t{ \"40Ohms\", I3C_HUB_DT_IO_STRENGTH_40_OHM },\n+\t{ \"50Ohms\", I3C_HUB_DT_IO_STRENGTH_50_OHM },\n+};\n+\n+/* Global mutex for serializing regmap access across all i3c hubs. */\n+static DEFINE_MUTEX(i3c_hub_regmap_mutex);\n+\n+static u8 i3c_hub_ldo_dt_to_reg(u8 dt_value)\n+{\n+\tswitch (dt_value) {\n+\tcase I3C_HUB_DT_LDO_VOLT_1_1V:\n+\t\treturn LDO_VOLTAGE_1_1V;\n+\tcase I3C_HUB_DT_LDO_VOLT_1_2V:\n+\t\treturn LDO_VOLTAGE_1_2V;\n+\tcase I3C_HUB_DT_LDO_VOLT_1_8V:\n+\t\treturn LDO_VOLTAGE_1_8V;\n+\tdefault:\n+\t\treturn LDO_VOLTAGE_1_0V;\n+\t}\n+}\n+\n+static u8 i3c_hub_pullup_dt_to_reg(u8 dt_value)\n+{\n+\tswitch (dt_value) {\n+\tcase I3C_HUB_DT_PULLUP_250R:\n+\t\treturn PULLUP_250R;\n+\tcase I3C_HUB_DT_PULLUP_500R:\n+\t\treturn PULLUP_500R;\n+\tcase I3C_HUB_DT_PULLUP_1K:\n+\t\treturn PULLUP_1K;\n+\tdefault:\n+\t\treturn PULLUP_2K;\n+\t}\n+}\n+\n+static u8 i3c_hub_io_strength_dt_to_reg(u8 dt_value)\n+{\n+\tswitch (dt_value) {\n+\tcase I3C_HUB_DT_IO_STRENGTH_50_OHM:\n+\t\treturn IO_STRENGTH_50_OHM;\n+\tcase I3C_HUB_DT_IO_STRENGTH_40_OHM:\n+\t\treturn IO_STRENGTH_40_OHM;\n+\tcase I3C_HUB_DT_IO_STRENGTH_30_OHM:\n+\t\treturn IO_STRENGTH_30_OHM;\n+\tdefault:\n+\t\treturn IO_STRENGTH_20_OHM;\n+\t}\n+}\n+\n+static void i3c_hub_of_get_setting(struct device *dev,\n+\t\t\t\t const struct device_node *node,\n+\t\t\t\t const char *setting_name,\n+\t\t\t\t const struct hub_setting settings[],\n+\t\t\t\t const u8 settings_count, u8 *setting_value)\n+{\n+\tconst char *sval;\n+\tint ret;\n+\tint i;\n+\n+\tret = of_property_read_string(node, setting_name, &sval);\n+\tif (ret) {\n+\t\t/* Lack of property is not considered as a problem. */\n+\t\tif (ret != -EINVAL)\n+\t\t\tdev_warn(\n+\t\t\t\tdev,\n+\t\t\t\t\"No setting or invalid setting for %s, err=%i\\n\",\n+\t\t\t\tsetting_name, ret);\n+\t\treturn;\n+\t}\n+\n+\tfor (i = 0; i < settings_count; ++i) {\n+\t\tconst struct hub_setting *const setting = &settings[i];\n+\n+\t\tif (!strcmp(setting->name, sval)) {\n+\t\t\t*setting_value = setting->value;\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\tdev_warn(dev, \"Unknown setting for %s: '%s'\\n\", setting_name, sval);\n+}\n+\n+static bool i3c_hub_smbus_validate_clock_frequency(u32 hz)\n+{\n+\tswitch (hz) {\n+\tcase 100000:\n+\tcase 200000:\n+\tcase 400000:\n+\tcase 1000000:\n+\t\treturn true;\n+\tdefault:\n+\t\treturn false;\n+\t}\n+}\n+\n+static inline u8 i3c_hub_smbus_rate_bits_from_hz(u32 hz)\n+{\n+\tswitch (hz) {\n+\tcase 100000:\n+\t\treturn I3C_HUB_SMBUS_100kHz;\n+\tcase 200000:\n+\t\treturn I3C_HUB_SMBUS_200kHz;\n+\tcase 1000000:\n+\t\treturn I3C_HUB_SMBUS_1000kHz;\n+\tdefault:\n+\t\treturn I3C_HUB_SMBUS_400kHz;\n+\t}\n+}\n+\n+static void i3c_hub_tp_of_get_setting(struct device *dev,\n+\t\t\t\t const struct device_node *node,\n+\t\t\t\t struct tp_setting tp_setting[])\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\tstruct device_node *tp_node;\n+\tu32 id, val;\n+\n+\tfor_each_available_child_of_node(node, tp_node) {\n+\t\tif (!tp_node->name || of_node_cmp(tp_node->name, \"target-port\"))\n+\t\t\tcontinue;\n+\n+\t\tif (!tp_node->full_name ||\n+\t\t (sscanf(tp_node->full_name, \"target-port@%u\", &id) != 1)) {\n+\t\t\tdev_warn(dev,\n+\t\t\t\t \"Invalid target port node found in DT: %s\\n\",\n+\t\t\t\t tp_node->full_name);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (id >= priv->dev_info->n_ports) {\n+\t\t\tdev_warn(dev,\n+\t\t\t\t \"Invalid target port index found in DT: %i\\n\",\n+\t\t\t\t id);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tpriv->smbus_port_adapter[id].of_node = tp_node;\n+\t\ti3c_hub_of_get_setting(dev, tp_node, \"mode\", tp_mode_settings,\n+\t\t\t\t ARRAY_SIZE(tp_mode_settings),\n+\t\t\t\t &tp_setting[id].mode);\n+\t\ti3c_hub_of_get_setting(dev, tp_node, \"pullup\",\n+\t\t\t\t tp_pullup_settings,\n+\t\t\t\t ARRAY_SIZE(tp_pullup_settings),\n+\t\t\t\t &tp_setting[id].pullup_en);\n+\t\ti3c_hub_of_get_setting(dev, tp_node, \"io-mode\",\n+\t\t\t\t tp_io_mode_settings,\n+\t\t\t\t ARRAY_SIZE(tp_io_mode_settings),\n+\t\t\t\t &tp_setting[id].io_mode);\n+\t\ttp_setting[id].always_enable =\n+\t\t\tof_property_read_bool(tp_node, \"always-enable\");\n+\t\tif (!of_property_read_u32(tp_node, \"polling-interval-ms\", &val))\n+\t\t\ttp_setting[id].poll_interval_ms = val;\n+\n+\t\tif (!of_property_read_u32(tp_node, \"clock-frequency\", &val)) {\n+\t\t\tif (i3c_hub_smbus_validate_clock_frequency(val))\n+\t\t\t\ttp_setting[id].clock_frequency = val;\n+\t\t\telse\n+\t\t\t\tdev_warn(\n+\t\t\t\t\tdev,\n+\t\t\t\t\t\"Unsupported TP%d smbus clock-frequency: %u Hz, using default %u Hz\\n\",\n+\t\t\t\t\tid, val,\n+\t\t\t\t\ttp_setting[id].clock_frequency);\n+\t\t}\n+\t}\n+}\n+\n+static void i3c_hub_of_get_conf_static(struct device *dev,\n+\t\t\t\t const struct device_node *node)\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\tu8 val = 0;\n+\n+\ti3c_hub_of_get_setting(dev, node, \"cp0-ldo-en\", ldo_en_settings,\n+\t\t\t ARRAY_SIZE(ldo_en_settings),\n+\t\t\t &priv->settings.cp0_ldo_en);\n+\ti3c_hub_of_get_setting(dev, node, \"cp1-ldo-en\", ldo_en_settings,\n+\t\t\t ARRAY_SIZE(ldo_en_settings),\n+\t\t\t &priv->settings.cp1_ldo_en);\n+\ti3c_hub_of_get_setting(dev, node, \"cp0-ldo-volt\", ldo_volt_settings,\n+\t\t\t ARRAY_SIZE(ldo_volt_settings),\n+\t\t\t &priv->settings.cp0_ldo_volt);\n+\ti3c_hub_of_get_setting(dev, node, \"cp1-ldo-volt\", ldo_volt_settings,\n+\t\t\t ARRAY_SIZE(ldo_volt_settings),\n+\t\t\t &priv->settings.cp1_ldo_volt);\n+\ti3c_hub_of_get_setting(dev, node, \"tp0145-ldo-en\", ldo_en_settings,\n+\t\t\t ARRAY_SIZE(ldo_en_settings),\n+\t\t\t &priv->settings.tp0145_ldo_en);\n+\ti3c_hub_of_get_setting(dev, node, \"tp2367-ldo-en\", ldo_en_settings,\n+\t\t\t ARRAY_SIZE(ldo_en_settings),\n+\t\t\t &priv->settings.tp2367_ldo_en);\n+\ti3c_hub_of_get_setting(dev, node, \"tp0145-ldo-volt\", ldo_volt_settings,\n+\t\t\t ARRAY_SIZE(ldo_volt_settings),\n+\t\t\t &priv->settings.tp0145_ldo_volt);\n+\ti3c_hub_of_get_setting(dev, node, \"tp2367-ldo-volt\", ldo_volt_settings,\n+\t\t\t ARRAY_SIZE(ldo_volt_settings),\n+\t\t\t &priv->settings.tp2367_ldo_volt);\n+\ti3c_hub_of_get_setting(dev, node, \"tp0145-pullup\", pullup_settings,\n+\t\t\t ARRAY_SIZE(pullup_settings),\n+\t\t\t &priv->settings.tp0145_pullup);\n+\ti3c_hub_of_get_setting(dev, node, \"tp2367-pullup\", pullup_settings,\n+\t\t\t ARRAY_SIZE(pullup_settings),\n+\t\t\t &priv->settings.tp2367_pullup);\n+\ti3c_hub_of_get_setting(dev, node, \"cp0-io-strength\",\n+\t\t\t io_strength_settings,\n+\t\t\t ARRAY_SIZE(io_strength_settings),\n+\t\t\t &priv->settings.cp0_io_strength);\n+\ti3c_hub_of_get_setting(dev, node, \"cp1-io-strength\",\n+\t\t\t io_strength_settings,\n+\t\t\t ARRAY_SIZE(io_strength_settings),\n+\t\t\t &priv->settings.cp1_io_strength);\n+\ti3c_hub_of_get_setting(dev, node, \"tp0145-io-strength\",\n+\t\t\t io_strength_settings,\n+\t\t\t ARRAY_SIZE(io_strength_settings),\n+\t\t\t &priv->settings.tp0145_io_strength);\n+\ti3c_hub_of_get_setting(dev, node, \"tp2367-io-strength\",\n+\t\t\t io_strength_settings,\n+\t\t\t ARRAY_SIZE(io_strength_settings),\n+\t\t\t &priv->settings.tp2367_io_strength);\n+\n+\tpriv->settings.hub_net_always_i3c =\n+\t\tof_property_read_bool(node, \"hub-net-always-i3c\");\n+\n+\tif (!of_property_read_u8(node, \"tp-scl-h-ack-cycles\", &val))\n+\t\tpriv->settings.tp_scl_h_ack_cycles = val;\n+\n+\ti3c_hub_tp_of_get_setting(dev, node, priv->settings.tp);\n+\n+\tpriv->settings.handshake_optimize =\n+\t\tof_property_read_bool(node, \"handshake-optimize\");\n+\n+\tif (!of_property_read_u8(node, \"fast-drv-h-add-cycles\", &val))\n+\t\tpriv->settings.fast_drv_h_add_cycles = val;\n+\n+\tpriv->settings.fast_rson_en =\n+\t\tof_property_read_bool(node, \"fast-rson-en\");\n+\n+\tpriv->settings.tp_od_vol_optimize =\n+\t\tof_property_read_bool(node, \"tp-od-vol-optimize\");\n+\n+\tpriv->settings.tp_od_vref_optimize =\n+\t\tof_property_read_bool(node, \"tp-od-vref-optimize\");\n+}\n+\n+static const struct i3c_hub_dev_info *\n+i3c_hub_lookup_dev_info(struct i3c_hub *priv)\n+{\n+\tint i, ret;\n+\tu16 part_id = 0;\n+\tu32 val = 0;\n+\n+\tret = regmap_read(priv->regmap, I3C_HUB_DEV_INFO_0, &val);\n+\tif (ret)\n+\t\treturn ERR_PTR(ret);\n+\n+\tpart_id = (val & 0xFF) << 8;\n+\n+\tret = regmap_read(priv->regmap, I3C_HUB_DEV_REV, &val);\n+\tif (ret)\n+\t\treturn ERR_PTR(ret);\n+\n+\tpart_id |= I3C_HUB_DEV_REV_LDO_GET(val);\n+\n+\tfor (i = 0; i < ARRAY_SIZE(i3c_hub_dev_info_table); i++) {\n+\t\tif (i3c_hub_dev_info_table[i].part_id == part_id)\n+\t\t\treturn &i3c_hub_dev_info_table[i];\n+\t}\n+\treturn &i3c_hub_dev_info_unknown;\n+}\n+\n+static void i3c_hub_of_default_configuration(struct device *dev)\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\tint id;\n+\n+\tpriv->settings.cp0_ldo_en = I3C_HUB_DT_LDO_NOT_DEFINED;\n+\tpriv->settings.cp1_ldo_en = I3C_HUB_DT_LDO_NOT_DEFINED;\n+\tpriv->settings.cp0_ldo_volt = I3C_HUB_DT_LDO_VOLT_NOT_DEFINED;\n+\tpriv->settings.cp1_ldo_volt = I3C_HUB_DT_LDO_VOLT_NOT_DEFINED;\n+\tpriv->settings.tp0145_ldo_en = I3C_HUB_DT_LDO_NOT_DEFINED;\n+\tpriv->settings.tp2367_ldo_en = I3C_HUB_DT_LDO_NOT_DEFINED;\n+\tpriv->settings.tp0145_ldo_volt = I3C_HUB_DT_LDO_VOLT_NOT_DEFINED;\n+\tpriv->settings.tp2367_ldo_volt = I3C_HUB_DT_LDO_VOLT_NOT_DEFINED;\n+\tpriv->settings.tp0145_pullup = I3C_HUB_DT_PULLUP_NOT_DEFINED;\n+\tpriv->settings.tp2367_pullup = I3C_HUB_DT_PULLUP_NOT_DEFINED;\n+\tpriv->settings.cp0_io_strength = I3C_HUB_DT_IO_STRENGTH_NOT_DEFINED;\n+\tpriv->settings.cp1_io_strength = I3C_HUB_DT_IO_STRENGTH_NOT_DEFINED;\n+\tpriv->settings.tp0145_io_strength = I3C_HUB_DT_IO_STRENGTH_NOT_DEFINED;\n+\tpriv->settings.tp2367_io_strength = I3C_HUB_DT_IO_STRENGTH_NOT_DEFINED;\n+\tpriv->settings.hub_net_always_i3c = false;\n+\tpriv->settings.tp_scl_h_ack_cycles = 0;\n+\tpriv->settings.handshake_optimize = false;\n+\tpriv->settings.fast_drv_h_add_cycles = 3;\n+\tpriv->settings.fast_rson_en = false;\n+\tpriv->settings.tp_od_vol_optimize = false;\n+\tpriv->settings.tp_od_vref_optimize = false;\n+\n+\tfor (id = 0; id < I3C_HUB_TP_MAX_COUNT; ++id) {\n+\t\tpriv->settings.tp[id].mode = I3C_HUB_DT_TP_MODE_NOT_DEFINED;\n+\t\tpriv->settings.tp[id].pullup_en =\n+\t\t\tI3C_HUB_DT_TP_PULLUP_NOT_DEFINED;\n+\t\tpriv->settings.tp[id].io_mode =\n+\t\t\tI3C_HUB_DT_TP_IO_MODE_NOT_DEFINED;\n+\t\tpriv->settings.tp[id].poll_interval_ms = 0;\n+\t\tpriv->settings.tp[id].clock_frequency = 400000;\n+\t}\n+}\n+\n+static int i3c_hub_hw_configure_pullup(struct device *dev)\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\tu8 mask = 0, value = 0;\n+\n+\tif (priv->settings.tp0145_pullup != I3C_HUB_DT_PULLUP_NOT_DEFINED) {\n+\t\tmask |= TP0145_PULLUP_CONF_MASK;\n+\t\tvalue |= TP0145_PULLUP_CONF(\n+\t\t\ti3c_hub_pullup_dt_to_reg(priv->settings.tp0145_pullup));\n+\t}\n+\n+\tif (priv->settings.tp2367_pullup != I3C_HUB_DT_PULLUP_NOT_DEFINED) {\n+\t\tmask |= TP2367_PULLUP_CONF_MASK;\n+\t\tvalue |= TP2367_PULLUP_CONF(\n+\t\t\ti3c_hub_pullup_dt_to_reg(priv->settings.tp2367_pullup));\n+\t}\n+\n+\treturn regmap_update_bits(priv->regmap, I3C_HUB_LDO_AND_PULLUP_CONF,\n+\t\t\t\t mask, value);\n+}\n+\n+static int i3c_hub_hw_configure_ldo(struct device *dev)\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\tu8 ldo_config_mask = 0, ldo_config_val = 0;\n+\tu8 ldo_disable_mask = 0, ldo_en_val = 0;\n+\tu32 reg_val;\n+\tint ret;\n+\tu8 val;\n+\n+\t/* Enable or Disable LDO's. If there is no DT entry - disable LDO for safety reasons */\n+\tif (priv->settings.cp0_ldo_en == I3C_HUB_DT_LDO_ENABLED)\n+\t\tldo_en_val |= CP0_LDO_EN;\n+\tif (priv->settings.cp1_ldo_en == I3C_HUB_DT_LDO_ENABLED)\n+\t\tldo_en_val |= CP1_LDO_EN;\n+\tif (priv->settings.tp0145_ldo_en == I3C_HUB_DT_LDO_ENABLED)\n+\t\tldo_en_val |= TP0145_LDO_EN;\n+\tif (priv->settings.tp2367_ldo_en == I3C_HUB_DT_LDO_ENABLED)\n+\t\tldo_en_val |= TP2367_LDO_EN;\n+\n+\t/* Get current LDOs configuration */\n+\tret = regmap_read(priv->regmap, I3C_HUB_LDO_CONF, ®_val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* LDOs Voltage level (Skip if not defined in the DT)\n+\t * Set the mask only if there is a change from current value\n+\t */\n+\tif (priv->settings.cp0_ldo_volt != I3C_HUB_DT_LDO_VOLT_NOT_DEFINED) {\n+\t\tval = CP0_LDO_VOLTAGE(\n+\t\t\ti3c_hub_ldo_dt_to_reg(priv->settings.cp0_ldo_volt));\n+\t\tif ((reg_val & CP0_LDO_VOLTAGE_MASK) != val) {\n+\t\t\tldo_config_mask |= CP0_LDO_VOLTAGE_MASK;\n+\t\t\tldo_disable_mask |= CP0_LDO_EN;\n+\t\t\tldo_config_val |= val;\n+\t\t}\n+\t}\n+\tif (priv->settings.cp1_ldo_volt != I3C_HUB_DT_LDO_VOLT_NOT_DEFINED) {\n+\t\tval = CP1_LDO_VOLTAGE(\n+\t\t\ti3c_hub_ldo_dt_to_reg(priv->settings.cp1_ldo_volt));\n+\t\tif ((reg_val & CP1_LDO_VOLTAGE_MASK) != val) {\n+\t\t\tldo_config_mask |= CP1_LDO_VOLTAGE_MASK;\n+\t\t\tldo_disable_mask |= CP1_LDO_EN;\n+\t\t\tldo_config_val |= val;\n+\t\t}\n+\t}\n+\tif (priv->settings.tp0145_ldo_volt != I3C_HUB_DT_LDO_VOLT_NOT_DEFINED) {\n+\t\tval = TP0145_LDO_VOLTAGE(\n+\t\t\ti3c_hub_ldo_dt_to_reg(priv->settings.tp0145_ldo_volt));\n+\t\tif ((reg_val & TP0145_LDO_VOLTAGE_MASK) != val) {\n+\t\t\tldo_config_mask |= TP0145_LDO_VOLTAGE_MASK;\n+\t\t\tldo_disable_mask |= TP0145_LDO_EN;\n+\t\t\tldo_config_val |= val;\n+\t\t}\n+\t}\n+\tif (priv->settings.tp2367_ldo_volt != I3C_HUB_DT_LDO_VOLT_NOT_DEFINED) {\n+\t\tval = TP2367_LDO_VOLTAGE(\n+\t\t\ti3c_hub_ldo_dt_to_reg(priv->settings.tp2367_ldo_volt));\n+\t\tif ((reg_val & TP2367_LDO_VOLTAGE_MASK) != val) {\n+\t\t\tldo_config_mask |= TP2367_LDO_VOLTAGE_MASK;\n+\t\t\tldo_disable_mask |= TP2367_LDO_EN;\n+\t\t\tldo_config_val |= val;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * Update LDO voltage configuration only if value is changed from already existing register\n+\t * value. It is a good practice to disable the LDO's before making any voltage changes.\n+\t * Presence of config mask indicates voltage change to be applied.\n+\t */\n+\tif (ldo_config_mask) {\n+\t\t/* Disable LDO's before making voltage changes */\n+\t\tret = regmap_update_bits(priv->regmap,\n+\t\t\t\t\t I3C_HUB_LDO_AND_PULLUP_CONF,\n+\t\t\t\t\t ldo_disable_mask, 0);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\t/* Update the LDOs configuration */\n+\t\tret = regmap_update_bits(priv->regmap, I3C_HUB_LDO_CONF,\n+\t\t\t\t\t ldo_config_mask, ldo_config_val);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\t/* Update the LDOs Enable/disable register. This will enable only LDOs enabled in DT */\n+\treturn regmap_update_bits(priv->regmap, I3C_HUB_LDO_AND_PULLUP_CONF,\n+\t\t\t\t LDO_ENABLE_DISABLE_MASK, ldo_en_val);\n+}\n+\n+static int i3c_hub_hw_configure_io_strength(struct device *dev)\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\tu8 mask_all = 0, val_all = 0;\n+\tu32 reg_val;\n+\tu8 val;\n+\tstruct dt_settings tmp;\n+\tint ret;\n+\n+\t/* Get IO strength configuration to figure out what needs to be changed */\n+\tret = regmap_read(priv->regmap, I3C_HUB_IO_STRENGTH, ®_val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\ttmp = priv->settings;\n+\tif (tmp.cp0_io_strength != I3C_HUB_DT_IO_STRENGTH_NOT_DEFINED) {\n+\t\tval = CP0_IO_STRENGTH(\n+\t\t\ti3c_hub_io_strength_dt_to_reg(tmp.cp0_io_strength));\n+\t\tmask_all |= CP0_IO_STRENGTH_MASK;\n+\t\tval_all |= val;\n+\t}\n+\tif (tmp.cp1_io_strength != I3C_HUB_DT_IO_STRENGTH_NOT_DEFINED) {\n+\t\tval = CP1_IO_STRENGTH(\n+\t\t\ti3c_hub_io_strength_dt_to_reg(tmp.cp1_io_strength));\n+\t\tmask_all |= CP1_IO_STRENGTH_MASK;\n+\t\tval_all |= val;\n+\t}\n+\tif (tmp.tp0145_io_strength != I3C_HUB_DT_IO_STRENGTH_NOT_DEFINED) {\n+\t\tval = TP0145_IO_STRENGTH(\n+\t\t\ti3c_hub_io_strength_dt_to_reg(tmp.tp0145_io_strength));\n+\t\tmask_all |= TP0145_IO_STRENGTH_MASK;\n+\t\tval_all |= val;\n+\t}\n+\tif (tmp.tp2367_io_strength != I3C_HUB_DT_IO_STRENGTH_NOT_DEFINED) {\n+\t\tval = TP2367_IO_STRENGTH(\n+\t\t\ti3c_hub_io_strength_dt_to_reg(tmp.tp2367_io_strength));\n+\t\tmask_all |= TP2367_IO_STRENGTH_MASK;\n+\t\tval_all |= val;\n+\t}\n+\n+\t/* Set IO strength if required */\n+\treturn regmap_update_bits(priv->regmap, I3C_HUB_IO_STRENGTH, mask_all,\n+\t\t\t\t val_all);\n+}\n+\n+static int i3c_hub_hw_configure_tp(struct device *dev)\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\tu8 pullup_mask = 0, pullup_val = 0;\n+\tu8 smbus_mask = 0, smbus_val = 0;\n+\tu8 gpio_mask = 0, gpio_val = 0;\n+\tu8 i3c_mask = 0, i3c_val = 0;\n+\tu8 io_mode_mask = 0, io_mode_val = 0;\n+\tint ret;\n+\tint i, index;\n+\n+\tmemset(priv->gpio.port_to_index, -1, sizeof(priv->gpio.port_to_index));\n+\n+\tfor (i = 0; i < priv->dev_info->n_ports; ++i) {\n+\t\tif (priv->settings.tp[i].mode !=\n+\t\t I3C_HUB_DT_TP_MODE_NOT_DEFINED) {\n+\t\t\ti3c_mask |= TPn_NET_CON(i);\n+\t\t\tsmbus_mask |= TPn_SMBUS_MODE_EN(i);\n+\t\t\tgpio_mask |= TPn_GPIO_MODE_EN(i);\n+\t\t\tio_mode_mask |= TPn_IO_MODE_CON(i);\n+\n+\t\t\tif (priv->settings.tp[i].mode ==\n+\t\t\t I3C_HUB_DT_TP_MODE_I3C) {\n+\t\t\t\ti3c_val |= TPn_NET_CON(i);\n+\t\t\t} else if (priv->settings.tp[i].mode ==\n+\t\t\t\t I3C_HUB_DT_TP_MODE_SMBUS) {\n+\t\t\t\tsmbus_val |= TPn_SMBUS_MODE_EN(i);\n+\t\t\t} else if (priv->settings.tp[i].mode ==\n+\t\t\t\t I3C_HUB_DT_TP_MODE_GPIO) {\n+\t\t\t\tgpio_val |= TPn_GPIO_MODE_EN(i);\n+\t\t\t\tpriv->gpio.nums += GPIO_BANK_SZ;\n+\t\t\t\tindex = priv->gpio.nums / GPIO_BANK_SZ - 1;\n+\t\t\t\tpriv->gpio.tp[index] = i;\n+\t\t\t\tpriv->gpio.port_to_index[i] = index;\n+\t\t\t}\n+\t\t}\n+\t\tif (priv->settings.tp[i].pullup_en !=\n+\t\t I3C_HUB_DT_TP_PULLUP_NOT_DEFINED) {\n+\t\t\tpullup_mask |= TPn_PULLUP_EN(i);\n+\t\t\tif (priv->settings.tp[i].pullup_en ==\n+\t\t\t I3C_HUB_DT_TP_PULLUP_ENABLED)\n+\t\t\t\tpullup_val |= TPn_PULLUP_EN(i);\n+\t\t}\n+\t\tif (priv->settings.tp[i].io_mode !=\n+\t\t I3C_HUB_DT_TP_IO_MODE_NOT_DEFINED) {\n+\t\t\tif (priv->settings.tp[i].io_mode ==\n+\t\t\t I3C_HUB_DT_TP_IO_MODE_OD)\n+\t\t\t\tio_mode_val |= TPn_IO_MODE_CON(i);\n+\t\t} else if (priv->settings.tp[i].mode ==\n+\t\t\t I3C_HUB_DT_TP_MODE_SMBUS) {\n+\t\t\tio_mode_val |= TPn_IO_MODE_CON(i);\n+\t\t}\n+\t}\n+\n+\tret = regmap_update_bits(priv->regmap, I3C_HUB_TP_IO_MODE_CONF,\n+\t\t\t\t io_mode_mask, io_mode_val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = regmap_update_bits(priv->regmap, I3C_HUB_TP_PULLUP_EN,\n+\t\t\t\t pullup_mask, pullup_val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = regmap_update_bits(priv->regmap, I3C_HUB_TP_SMBUS_AGNT_EN,\n+\t\t\t\t smbus_mask, smbus_val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = regmap_update_bits(priv->regmap, I3C_HUB_TP_GPIO_MODE_EN,\n+\t\t\t\t gpio_mask, gpio_val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Request for HUB Network connection in case any TP is configured in I3C mode */\n+\tif (i3c_val) {\n+\t\tret = regmap_write(priv->regmap, I3C_HUB_CP_MUX_SET,\n+\t\t\t\t CONTROLLER_PORT_MUX_REQ);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\t/* TODO: verify if connection is done */\n+\t}\n+\n+\t/* Enable TP here in case TP was configured */\n+\tret = regmap_update_bits(priv->regmap, I3C_HUB_TP_ENABLE,\n+\t\t\t\t i3c_mask | smbus_mask | gpio_mask,\n+\t\t\t\t i3c_val | smbus_val | gpio_val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn regmap_write(priv->regmap, I3C_HUB_TP_NET_CON_CONF, i3c_val);\n+}\n+\n+static int i3c_hub_hw_configure_misc(struct device *dev)\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\tint ret;\n+\tu8 reg = I3C_HUB_TARGET_DA_FLAG_BYTE_BASE;\n+\tu8 val[I3C_HUB_TARGET_DA_FLAG_BYTE_COUNT];\n+\n+\tif (!priv->settings.hub_net_always_i3c)\n+\t\treturn 0;\n+\n+\tmemset(val, 0xff, I3C_HUB_TARGET_DA_FLAG_BYTE_COUNT);\n+\n+\tret = regmap_update_bits(priv->regmap, I3C_HUB_NET_OPER_MODE_CONF,\n+\t\t\t\t I3C_HUB_NET_ALWAYS_I3C_EN,\n+\t\t\t\t I3C_HUB_NET_ALWAYS_I3C_EN);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = regmap_bulk_write(priv->regmap, reg, val,\n+\t\t\t\tI3C_HUB_TARGET_DA_FLAG_BYTE_COUNT);\n+\treturn ret;\n+}\n+\n+typedef int (*i3c_hub_cfg_fn)(struct i3c_hub *priv);\n+\n+static int i3c_hub_hw_cfg_with_page(struct i3c_hub *priv, u8 page,\n+\t\t\t\t i3c_hub_cfg_fn op)\n+{\n+\tint ret;\n+\n+\tif (!op)\n+\t\treturn -EINVAL;\n+\n+\tmutex_lock(&priv->page_mutex);\n+\tret = regmap_write(priv->regmap, I3C_HUB_PAGE_PTR, page);\n+\tif (ret)\n+\t\tgoto unlock;\n+\n+\tret = op(priv);\n+unlock:\n+\tregmap_write(priv->regmap, I3C_HUB_PAGE_PTR, 0x00);\n+\tmutex_unlock(&priv->page_mutex);\n+\treturn ret;\n+}\n+\n+static int i3c_hub_cfg_op_fuse_latch(struct i3c_hub *priv)\n+{\n+\tint ret;\n+\n+\tif (priv->settings.handshake_optimize) {\n+\t\tret = regmap_update_bits(priv->regmap, I3C_HUB_EFUSE_OFFSET_9E,\n+\t\t\t\t\t I3C_HUB_IBI_ACK_RD_CYCLE_MASK,\n+\t\t\t\t\t I3C_HUB_IBI_ACK_RD_CYCLE_VAL);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = regmap_update_bits(priv->regmap, I3C_HUB_EFUSE_OFFSET_A3,\n+\t\t\t\t I3C_HUB_FAST_DRV_LOOP_DIS,\n+\t\t\t\t I3C_HUB_FAST_DRV_LOOP_DIS);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (priv->settings.tp_od_vol_optimize) {\n+\t\tret = regmap_update_bits(priv->regmap, I3C_HUB_EFUSE_OFFSET_9D,\n+\t\t\t\t\t I3C_HUB_TP_OD_VOL_LEVEL,\n+\t\t\t\t\t I3C_HUB_TP_OD_VOL_LEVEL);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (priv->settings.tp_od_vref_optimize) {\n+\t\tret = regmap_update_bits(priv->regmap, I3C_HUB_EFUSE_OFFSET_9D,\n+\t\t\t\t\t I3C_HUB_TP_OD_VREF,\n+\t\t\t\t\t I3C_HUB_TP_OD_VREF);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = regmap_update_bits(priv->regmap, I3C_HUB_EFUSE_OFFSET_9E,\n+\t\t\t\t I3C_HUB_FAST_DRV_H_ADD_CYCLE_MASK,\n+\t\t\t\t I3C_HUB_FAST_DRV_H_ADD_CYCLE_VAL(\n+\t\t\t\t\t priv->settings.fast_drv_h_add_cycles));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = regmap_update_bits(\n+\t\tpriv->regmap, I3C_HUB_EFUSE_OFFSET_A0, I3C_HUB_FAST_RSON_EN,\n+\t\tpriv->settings.fast_rson_en ? I3C_HUB_FAST_RSON_EN : 0);\n+\treturn ret;\n+}\n+\n+static int i3c_hub_hw_configure_fuse_latch(struct device *dev)\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\n+\treturn i3c_hub_hw_cfg_with_page(priv, I3C_HUB_EFUSE_PAGE,\n+\t\t\t\t\ti3c_hub_cfg_op_fuse_latch);\n+}\n+\n+static int i3c_hub_cfg_op_io(struct i3c_hub *priv)\n+{\n+\tint ret;\n+\tu8 reg = I3C_HUB_CFG_TP_SCL_L_ACK_CLK;\n+\n+\t/* cfg tp scl low ack clk */\n+\tret = regmap_write(priv->regmap, reg,\n+\t\t\t I3C_HUB_CFG_TP_SCL_L_ACK_CLK_EN |\n+\t\t\t\t I3C_HUB_CFG_TP_SCL_L_ACK_CLK_COUNT_VAL);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* cfg tp scl high ack clk */\n+\treg = I3C_HUB_CFG_TP_SCL_H_ACK_CLK;\n+\tif (priv->settings.tp_scl_h_ack_cycles == 0)\n+\t\treturn 0;\n+\n+\tret = regmap_write(priv->regmap, reg,\n+\t\t\t I3C_HUB_CFG_TP_SCL_H_ACK_CLK_EN |\n+\t\t\t\t I3C_HUB_CFG_TP_SCL_H_ACK_CLK_COUNT_VAL(\n+\t\t\t\t\t priv->settings.tp_scl_h_ack_cycles));\n+\n+\treturn ret;\n+}\n+\n+static int i3c_hub_hw_configure_io(struct device *dev)\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\n+\treturn i3c_hub_hw_cfg_with_page(priv, I3C_HUB_IO_CTRL_PAGE,\n+\t\t\t\t\ti3c_hub_cfg_op_io);\n+}\n+\n+static int i3c_hub_configure_hw(struct device *dev)\n+{\n+\tint ret;\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\n+\tret = i3c_hub_hw_configure_ldo(dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = i3c_hub_hw_configure_io_strength(dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = i3c_hub_hw_configure_pullup(dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (priv->dev_info->part_id) {\n+\t\tret = i3c_hub_hw_configure_misc(dev);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tret = i3c_hub_hw_configure_fuse_latch(dev);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tret = i3c_hub_hw_configure_io(dev);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = i3c_hub_hw_configure_tp(dev);\n+\treturn ret;\n+}\n+\n+static void i3c_hub_of_get_conf_runtime(struct device *dev,\n+\t\t\t\t\tconst struct device_node *node)\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\tstruct device_node *i3c_node;\n+\tint i3c_id;\n+\tu8 tp_mask;\n+\n+\tfor_each_available_child_of_node(node, i3c_node) {\n+\t\tif (!i3c_node->full_name ||\n+\t\t (sscanf(i3c_node->full_name, \"i3c%i@%hhx\", &i3c_id,\n+\t\t\t &tp_mask) != 2))\n+\t\t\tcontinue;\n+\n+\t\tif (i3c_id < priv->dev_info->n_ports) {\n+\t\t\tpriv->logical_bus[i3c_id].available = true;\n+\t\t\tpriv->logical_bus[i3c_id].of_node = i3c_node;\n+\t\t\tpriv->logical_bus[i3c_id].tp_map = tp_mask;\n+\t\t\tpriv->logical_bus[i3c_id].priv = priv;\n+\t\t\tpriv->logical_bus[i3c_id].tp_id = i3c_id;\n+\t\t}\n+\t}\n+}\n+\n+static const struct i3c_device_id i3c_hub_ids[] = {\n+\tI3C_CLASS(I3C_DCR_HUB, NULL),\n+\t{},\n+};\n+\n+static int i3c_hub_read_id(struct device *dev)\n+{\n+\tstruct i3c_hub *priv = dev_get_drvdata(dev);\n+\tu32 reg_val;\n+\tint ret;\n+\n+\tret = regmap_read(priv->regmap, I3C_HUB_LDO_AND_CPSEL_STS, ®_val);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to read status register\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tpriv->hub_pin_sel_id = CP_SEL_PIN_INPUT_CODE_GET(reg_val);\n+\tpriv->hub_pin_cp1_id = CP_SDA1_SCL1_PINS_CODE_GET(reg_val);\n+\treturn 0;\n+}\n+\n+static struct device_node *i3c_hub_get_dt_hub_node(struct device_node *node,\n+\t\t\t\t\t\t struct i3c_hub *priv)\n+{\n+\tstruct device_node *hub_node_no_id = NULL;\n+\tstruct device_node *hub_node;\n+\tu32 hub_id;\n+\tu32 id_mask;\n+\tu32 dt_id;\n+\tu32 pin_id;\n+\tint found_id = 0;\n+\n+\tfor_each_available_child_of_node(node, hub_node) {\n+\t\tid_mask = 0;\n+\t\tpriv->hub_dt_sel_id = -1;\n+\t\tpriv->hub_dt_cp1_id = -1;\n+\n+\t\tif (strstr(hub_node->name, \"hub\")) {\n+\t\t\tif (!of_property_read_u32(hub_node, \"id\", &hub_id)) {\n+\t\t\t\tid_mask |= 0x0f;\n+\t\t\t\tpriv->hub_dt_sel_id = hub_id;\n+\t\t\t}\n+\n+\t\t\tif (!of_property_read_u32(hub_node, \"id-cp1\",\n+\t\t\t\t\t\t &hub_id)) {\n+\t\t\t\tid_mask |= 0xf0;\n+\t\t\t\tpriv->hub_dt_cp1_id = hub_id;\n+\t\t\t}\n+\n+\t\t\tdt_id = ((u32)priv->hub_dt_cp1_id & 0x0f) << 4 |\n+\t\t\t\t((u32)priv->hub_dt_sel_id & 0x0f);\n+\t\t\tpin_id = ((u32)priv->hub_pin_cp1_id & 0x0f) << 4 |\n+\t\t\t\t ((u32)priv->hub_pin_sel_id & 0x0f);\n+\n+\t\t\tif (id_mask != 0 &&\n+\t\t\t (dt_id & id_mask) == (pin_id & id_mask))\n+\t\t\t\tfound_id = 1;\n+\n+\t\t\tif (!found_id) {\n+\t\t\t\t/*\n+\t\t\t\t * Just keep reference to first HUB node with no ID in case no ID\n+\t\t\t\t * matching\n+\t\t\t\t */\n+\t\t\t\tif (!hub_node_no_id &&\n+\t\t\t\t priv->hub_dt_sel_id == -1 &&\n+\t\t\t\t priv->hub_dt_cp1_id == -1)\n+\t\t\t\t\thub_node_no_id = hub_node;\n+\t\t\t} else {\n+\t\t\t\treturn hub_node;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn hub_node_no_id;\n+}\n+\n+static int fops_access_reg_get(void *ctx, u64 *val)\n+{\n+\tstruct i3c_hub *priv = ctx;\n+\tu32 reg_val;\n+\tint ret;\n+\n+\tret = regmap_read(priv->regmap, priv->reg_addr, ®_val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t*val = reg_val & 0xFF;\n+\treturn 0;\n+}\n+\n+static int fops_access_reg_set(void *ctx, u64 val)\n+{\n+\tstruct i3c_hub *priv = ctx;\n+\n+\treturn regmap_write(priv->regmap, priv->reg_addr, val & 0xFF);\n+}\n+\n+DEFINE_DEBUGFS_ATTRIBUTE(fops_access_reg, fops_access_reg_get,\n+\t\t\t fops_access_reg_set, \"0x%llX\\n\");\n+\n+static int i3c_hub_debugfs_init(struct i3c_hub *priv, const char *hub_id)\n+{\n+\tstruct dentry *entry, *dt_conf_dir, *reg_dir;\n+\tstruct dt_settings *settings = NULL;\n+\tint i;\n+\n+\tentry = debugfs_create_dir(hub_id, NULL);\n+\tif (IS_ERR(entry))\n+\t\treturn PTR_ERR(entry);\n+\n+\tpriv->debug_dir = entry;\n+\n+\tif (priv->dev_info)\n+\t\tdebugfs_create_str(\"model\", 0400, priv->debug_dir,\n+\t\t\t\t (char **)&priv->dev_info->model);\n+\n+\tentry = debugfs_create_dir(\"dt-conf\", priv->debug_dir);\n+\tif (IS_ERR(entry))\n+\t\tgoto err_remove;\n+\n+\tdt_conf_dir = entry;\n+\n+\tsettings = &priv->settings;\n+\tdebugfs_create_u8(\"cp0-ldo-en\", 0400, dt_conf_dir,\n+\t\t\t &settings->cp0_ldo_en);\n+\tdebugfs_create_u8(\"cp1-ldo-en\", 0400, dt_conf_dir,\n+\t\t\t &settings->cp1_ldo_en);\n+\tdebugfs_create_u8(\"cp0-ldo-volt\", 0400, dt_conf_dir,\n+\t\t\t &settings->cp0_ldo_volt);\n+\tdebugfs_create_u8(\"cp1-ldo-volt\", 0400, dt_conf_dir,\n+\t\t\t &settings->cp1_ldo_volt);\n+\tdebugfs_create_u8(\"tp0145-ldo-en\", 0400, dt_conf_dir,\n+\t\t\t &settings->tp0145_ldo_en);\n+\tdebugfs_create_u8(\"tp2367-ldo-en\", 0400, dt_conf_dir,\n+\t\t\t &settings->tp2367_ldo_en);\n+\tdebugfs_create_u8(\"tp0145-ldo-volt\", 0400, dt_conf_dir,\n+\t\t\t &settings->tp0145_ldo_volt);\n+\tdebugfs_create_u8(\"tp2367-ldo-volt\", 0400, dt_conf_dir,\n+\t\t\t &settings->tp2367_ldo_volt);\n+\tdebugfs_create_u8(\"tp0145-pullup\", 0400, dt_conf_dir,\n+\t\t\t &settings->tp0145_pullup);\n+\tdebugfs_create_u8(\"tp2367-pullup\", 0400, dt_conf_dir,\n+\t\t\t &settings->tp2367_pullup);\n+\tdebugfs_create_bool(\"hub-net-always-i3c\", 0400, dt_conf_dir,\n+\t\t\t &settings->hub_net_always_i3c);\n+\tdebugfs_create_u8(\"tp-scl-h-ack-cycles\", 0400, dt_conf_dir,\n+\t\t\t &settings->tp_scl_h_ack_cycles);\n+\tdebugfs_create_bool(\"handshake-optimize\", 0400, dt_conf_dir,\n+\t\t\t &settings->handshake_optimize);\n+\tdebugfs_create_u8(\"fast-drv-h-add-cycles\", 0400, dt_conf_dir,\n+\t\t\t &settings->fast_drv_h_add_cycles);\n+\tdebugfs_create_bool(\"fast-rson-en\", 0400, dt_conf_dir,\n+\t\t\t &settings->fast_rson_en);\n+\tdebugfs_create_bool(\"tp-od-vol-optimize\", 0400, dt_conf_dir,\n+\t\t\t &settings->tp_od_vol_optimize);\n+\tdebugfs_create_bool(\"tp-od-vref-optimize\", 0400, dt_conf_dir,\n+\t\t\t &settings->tp_od_vref_optimize);\n+\n+\tfor (i = 0; i < priv->dev_info->n_ports; ++i) {\n+\t\tchar file_name[32];\n+\n+\t\tsprintf(file_name, \"tp%i.mode\", i);\n+\t\tdebugfs_create_u8(file_name, 0400, dt_conf_dir,\n+\t\t\t\t &settings->tp[i].mode);\n+\t\tsprintf(file_name, \"tp%i.pullup_en\", i);\n+\t\tdebugfs_create_u8(file_name, 0400, dt_conf_dir,\n+\t\t\t\t &settings->tp[i].pullup_en);\n+\t\tsprintf(file_name, \"tp%i.io_mode\", i);\n+\t\tdebugfs_create_u8(file_name, 0400, dt_conf_dir,\n+\t\t\t\t &settings->tp[i].io_mode);\n+\t\tsprintf(file_name, \"tp%i.poll_interval_ms\", i);\n+\t\tdebugfs_create_u32(file_name, 0400, dt_conf_dir,\n+\t\t\t\t &settings->tp[i].poll_interval_ms);\n+\t\tsprintf(file_name, \"tp%i.clock_frequency\", i);\n+\t\tdebugfs_create_u32(file_name, 0400, dt_conf_dir,\n+\t\t\t\t &settings->tp[i].clock_frequency);\n+\t}\n+\n+\tentry = debugfs_create_dir(\"reg\", priv->debug_dir);\n+\tif (IS_ERR(entry))\n+\t\tgoto err_remove;\n+\n+\treg_dir = entry;\n+\n+\tentry = debugfs_create_file_unsafe(\"access\", 0600, reg_dir, priv,\n+\t\t\t\t\t &fops_access_reg);\n+\tif (IS_ERR(entry))\n+\t\tgoto err_remove;\n+\n+\tdebugfs_create_u8(\"offset\", 0600, reg_dir, &priv->reg_addr);\n+\n+\treturn 0;\n+\n+err_remove:\n+\tdebugfs_remove_recursive(priv->debug_dir);\n+\treturn PTR_ERR(entry);\n+}\n+\n+static void i3c_hub_trans_pre_cb(struct logical_bus *bus)\n+{\n+\tstruct i3c_hub *priv = bus->priv;\n+\tstruct device *dev = i3cdev_to_dev(priv->i3cdev);\n+\tint ret;\n+\n+\tif (priv->settings.tp[bus->tp_id].always_enable)\n+\t\treturn;\n+\n+\tret = regmap_update_bits(priv->regmap, I3C_HUB_TP_NET_CON_CONF,\n+\t\t\t\t GENMASK(bus->tp_id, bus->tp_id), bus->tp_map);\n+\tif (ret)\n+\t\tdev_warn(dev, \"Failed to open Target Port(s)\\n\");\n+}\n+\n+static void i3c_hub_trans_post_cb(struct logical_bus *bus)\n+{\n+\tstruct i3c_hub *priv = bus->priv;\n+\tstruct device *dev = i3cdev_to_dev(priv->i3cdev);\n+\tint ret;\n+\n+\tif (priv->settings.tp[bus->tp_id].always_enable)\n+\t\treturn;\n+\n+\tret = regmap_update_bits(priv->regmap, I3C_HUB_TP_NET_CON_CONF,\n+\t\t\t\t GENMASK(bus->tp_id, bus->tp_id), 0x00);\n+\tif (ret)\n+\t\tdev_warn(dev, \"Failed to close Target Port(s)\\n\");\n+}\n+\n+static struct logical_bus *bus_from_i3c_desc(struct i3c_dev_desc *desc)\n+{\n+\tstruct i3c_master_controller *controller = i3c_dev_get_master(desc);\n+\n+\treturn container_of(controller, struct logical_bus, controller);\n+}\n+\n+static struct logical_bus *bus_from_i2c_desc(struct i2c_dev_desc *desc)\n+{\n+\tstruct i3c_master_controller *controller = i2c_dev_get_master(desc);\n+\n+\treturn container_of(controller, struct logical_bus, controller);\n+}\n+\n+static struct i3c_master_controller *\n+parent_from_controller(struct i3c_master_controller *controller)\n+{\n+\tstruct logical_bus *bus =\n+\t\tcontainer_of(controller, struct logical_bus, controller);\n+\n+\treturn bus->priv->driving_master;\n+}\n+\n+static struct i3c_master_controller *\n+parent_controller_from_i3c_desc(struct i3c_dev_desc *desc)\n+{\n+\tstruct i3c_master_controller *controller = i3c_dev_get_master(desc);\n+\tstruct logical_bus *bus =\n+\t\tcontainer_of(controller, struct logical_bus, controller);\n+\n+\treturn bus->priv->driving_master;\n+}\n+\n+static struct i3c_master_controller *\n+parent_controller_from_i2c_desc(struct i2c_dev_desc *desc)\n+{\n+\tstruct i3c_master_controller *controller = desc->common.master;\n+\tstruct logical_bus *bus =\n+\t\tcontainer_of(controller, struct logical_bus, controller);\n+\n+\treturn bus->priv->driving_master;\n+}\n+\n+static struct i3c_master_controller *\n+update_i3c_i2c_desc_parent(struct i3c_i2c_dev_desc *desc,\n+\t\t\t struct i3c_master_controller *parent)\n+{\n+\tstruct i3c_master_controller *orig_parent = desc->master;\n+\n+\tdesc->master = parent;\n+\n+\treturn orig_parent;\n+}\n+\n+static void restore_i3c_i2c_desc_parent(struct i3c_i2c_dev_desc *desc,\n+\t\t\t\t\tstruct i3c_master_controller *parent)\n+{\n+\tdesc->master = parent;\n+}\n+\n+static int i3c_hub_read_transaction_status(struct i3c_hub *priv, u8 target_port,\n+\t\t\t\t\t u8 target_port_status, u32 data_len)\n+{\n+\tunsigned int status_read;\n+\tint ret;\n+\tstruct i2c_adapter_group *smbus =\n+\t\t&priv->smbus_port_adapter[target_port];\n+\tu32 smbus_clk = priv->settings.tp[target_port].clock_frequency / 1000;\n+\tu8 status;\n+\tu8 ret_code;\n+\n+\tif (!priv->settings.tp[target_port].poll_interval_ms) {\n+\t\tret = wait_for_completion_timeout(&smbus->completion,\n+\t\t\t\t\t\t smbus->i2c.timeout);\n+\t\tif (!ret) {\n+\t\t\tdev_err(&priv->i3cdev->dev,\n+\t\t\t\t\"Status read timeout reached on target port %d\\n\",\n+\t\t\t\ttarget_port);\n+\t\t\treturn -ETIMEDOUT;\n+\t\t}\n+\n+\t\tstatus = (u8)smbus->status &\n+\t\t\t I3C_HUB_CONTROLLER_AGENT_STATUS_MASK;\n+\t} else {\n+\t\tret = regmap_read_poll_timeout(\n+\t\t\tpriv->regmap, target_port_status, status_read,\n+\t\t\t(u8)status_read & I3C_HUB_CONTROLLER_AGENT_FINISH_FLAG,\n+\t\t\tI3C_HUB_SMBUS_STATUS_READ_INTERVAL_US_CEIL(data_len,\n+\t\t\t\t\t\t\t\t smbus_clk),\n+\t\t\tjiffies_to_usecs(smbus->i2c.timeout));\n+\n+\t\tif (ret) {\n+\t\t\tdev_err(&priv->i3cdev->dev,\n+\t\t\t\t\"Status read timeout reached on target port %d\\n\",\n+\t\t\t\ttarget_port);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = regmap_write(priv->regmap, target_port_status,\n+\t\t\t\t I3C_HUB_CONTROLLER_AGENT_FINISH_FLAG);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tstatus = (u8)status_read & I3C_HUB_CONTROLLER_AGENT_STATUS_MASK;\n+\t}\n+\n+\tret_code = status >> I3C_HUB_CONTROLLER_AGENT_RET_CODE_SHIFT;\n+\n+\tswitch (ret_code) {\n+\tcase I3C_HUB_CONTROLLER_AGENT_RET_CODE_SUCCESS:\n+\t\treturn 0;\n+\tcase I3C_HUB_CONTROLLER_AGENT_RET_CODE_ADDRESS_NACK:\n+\t\tdev_dbg(&priv->i3cdev->dev,\n+\t\t\t\"TP%u SMBus: Address NACK (device not present)\\n\",\n+\t\t\ttarget_port);\n+\t\treturn -ENXIO;\n+\tcase I3C_HUB_CONTROLLER_AGENT_RET_CODE_DEVICE_BUSY:\n+\t\tdev_dbg(&priv->i3cdev->dev,\n+\t\t\t\"TP%u SMBus: Device busy (data NACK after address ACK)\\n\",\n+\t\t\ttarget_port);\n+\t\treturn -EREMOTEIO;\n+\tcase I3C_HUB_CONTROLLER_AGENT_RET_CODE_READ_NOT_READY:\n+\t\tdev_dbg(&priv->i3cdev->dev,\n+\t\t\t\"TP%u SMBus: Device read not ready (read address NACK after write)\\n\",\n+\t\t\ttarget_port);\n+\t\treturn -ENXIO;\n+\tcase I3C_HUB_CONTROLLER_AGENT_RET_CODE_SYNC_RECOVERED:\n+\t\tdev_dbg(&priv->i3cdev->dev,\n+\t\t\t\"TP%u SMBus: Sync issue recovered (SDA stuck low, recovered by 9 SCL pulses)\\n\",\n+\t\t\ttarget_port);\n+\t\treturn -EAGAIN;\n+\tcase I3C_HUB_CONTROLLER_AGENT_RET_CODE_SYNC_BUS_CLEAR:\n+\t\tdev_dbg(&priv->i3cdev->dev,\n+\t\t\t\"TP%u SMBus: Sync issue bus clear (recovered by SCL low 35ms)\\n\",\n+\t\t\ttarget_port);\n+\t\treturn -EAGAIN;\n+\tcase I3C_HUB_CONTROLLER_AGENT_RET_CODE_BUS_FAULT:\n+\t\tdev_err(&priv->i3cdev->dev,\n+\t\t\t\"TP%u SMBus: Bus fault (SDA stuck low remains after recovery)\\n\",\n+\t\t\ttarget_port);\n+\t\treturn -EIO;\n+\tcase I3C_HUB_CONTROLLER_AGENT_RET_CODE_ARBITRATION_LOST:\n+\t\tdev_dbg(&priv->i3cdev->dev, \"TP%u SMBus: Arbitration lost\\n\",\n+\t\t\ttarget_port);\n+\t\treturn -EAGAIN;\n+\tcase I3C_HUB_CONTROLLER_AGENT_RET_CODE_SCL_TIMEOUT:\n+\t\tdev_err(&priv->i3cdev->dev, \"TP%u SMBus: SCL timeout\\n\",\n+\t\t\ttarget_port);\n+\t\treturn -ETIMEDOUT;\n+\tdefault:\n+\t\tdev_err(&priv->i3cdev->dev,\n+\t\t\t\"TP%u SMBus: Reserved/unknown return code 0x%x\\n\",\n+\t\t\ttarget_port, ret_code);\n+\t\treturn -EIO;\n+\t}\n+}\n+\n+/*\n+ * i3c_hub_smbus_msg() - This starts a smbus transaction by writing a descriptor\n+ * and a message to the hub registers. Controller buffer page is determined by multiplying the\n+ * target port index by four and adding the base page number to it.\n+ * @hub: a pointer to the i3c hub main structure\n+ * @target_port: a number of the port where the transaction will happen\n+ * @xfers: i2c_msg struct received from the master_xfers callback\n+ * @nxfers_i: the number of the current message\n+ * @xfer_type: transfer type:\n+ * - I3C_HUB_SMBUS_XFER_WRITE (0): single write\n+ * - I3C_HUB_SMBUS_XFER_READ (1): single read\n+ * - I3C_HUB_SMBUS_XFER_WR_RD (2): write followed by read\n+ * (uses xfers[nxfers_i] as write and xfers[nxfers_i+1] as read)\n+ *\n+ * Return: 0 on success, negative errno on failure from hub status or regmap ops.\n+ * Note: for WR_RD the caller must ensure xfers[nxfers_i+1] exists, the address\n+ * matches, and write_len + read_len <= I3C_HUB_SMBUS_PAYLOAD_SIZE.\n+ */\n+static int i3c_hub_smbus_msg(struct i3c_hub *hub, struct i2c_msg *xfers,\n+\t\t\t u8 target_port, u8 nxfers_i, u8 xfer_type)\n+{\n+\tu8 transaction_type = I3C_HUB_SMBUS_400kHz;\n+\tu8 controller_buffer_page =\n+\t\tI3C_HUB_CONTROLLER_BUFFER_PAGE + 4 * target_port;\n+\tint write_length = 0, read_length = 0;\n+\tu8 target_port_status = I3C_HUB_TP0_SMBUS_AGNT_STS + target_port;\n+\tu8 target_port_code = BIT(target_port);\n+\tu8 rw_address = xfers[nxfers_i].addr << 1;\n+\tu8 desc[I3C_HUB_SMBUS_DESCRIPTOR_SIZE] = { 0 };\n+\tint ret = 0;\n+\n+\ttransaction_type = i3c_hub_smbus_rate_bits_from_hz(\n+\t\thub->settings.tp[target_port].clock_frequency);\n+\n+\tswitch (xfer_type) {\n+\tcase I3C_HUB_SMBUS_XFER_WRITE:\n+\t\twrite_length = xfers[nxfers_i].len;\n+\t\tbreak;\n+\tcase I3C_HUB_SMBUS_XFER_READ:\n+\t\tread_length = xfers[nxfers_i].len;\n+\t\trw_address |= BIT(0);\n+\t\tbreak;\n+\tcase I3C_HUB_SMBUS_XFER_WR_RD:\n+\t\twrite_length = xfers[nxfers_i].len;\n+\t\tread_length = xfers[nxfers_i + 1].len;\n+\t\ttransaction_type |= BIT(0);\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Assemble descriptor */\n+\tdesc[0] = rw_address;\n+\tdesc[1] = transaction_type;\n+\tdesc[2] = write_length;\n+\tdesc[3] = read_length;\n+\n+\tmutex_lock(&hub->page_mutex);\n+\tret = regmap_write(hub->regmap, I3C_HUB_PAGE_PTR,\n+\t\t\t controller_buffer_page);\n+\tif (ret)\n+\t\tgoto unlock;\n+\n+\tret = regmap_bulk_write(hub->regmap, I3C_HUB_CONTROLLER_AGENT_BUFF,\n+\t\t\t\tdesc, I3C_HUB_SMBUS_DESCRIPTOR_SIZE);\n+\tif (ret)\n+\t\tgoto unlock;\n+\n+\tif (write_length) {\n+\t\tret = regmap_bulk_write(hub->regmap,\n+\t\t\t\t\tI3C_HUB_CONTROLLER_AGENT_BUFF_DATA,\n+\t\t\t\t\txfers[nxfers_i].buf, write_length);\n+\t\tif (ret)\n+\t\t\tgoto unlock;\n+\t}\n+\n+\tret = regmap_write(hub->regmap, I3C_HUB_PAGE_PTR, 0x00);\n+\tmutex_unlock(&hub->page_mutex);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Start transaction */\n+\tret = regmap_write(hub->regmap, I3C_HUB_TP_SMBUS_AGNT_TRANS_START,\n+\t\t\t target_port_code);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Get transaction status */\n+\tret = i3c_hub_read_transaction_status(hub, target_port,\n+\t\t\t\t\t target_port_status,\n+\t\t\t\t\t write_length + read_length);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* if read_length is non-zero, read back the data */\n+\tif (read_length) {\n+\t\tmutex_lock(&hub->page_mutex);\n+\t\tret = regmap_write(hub->regmap, I3C_HUB_PAGE_PTR,\n+\t\t\t\t controller_buffer_page);\n+\t\tif (ret)\n+\t\t\tgoto unlock;\n+\n+\t\tif (xfer_type == I3C_HUB_SMBUS_XFER_READ) {\n+\t\t\tret = regmap_bulk_read(\n+\t\t\t\thub->regmap, I3C_HUB_CONTROLLER_AGENT_BUFF_DATA,\n+\t\t\t\txfers[nxfers_i].buf, read_length);\n+\t\t} else {\n+\t\t\tret = regmap_bulk_read(\n+\t\t\t\thub->regmap,\n+\t\t\t\tI3C_HUB_CONTROLLER_AGENT_BUFF_DATA +\n+\t\t\t\t\twrite_length,\n+\t\t\t\txfers[nxfers_i + 1].buf, read_length);\n+\t\t}\n+\t\tif (ret)\n+\t\t\tgoto unlock;\n+\n+\t\tret = regmap_write(hub->regmap, I3C_HUB_PAGE_PTR, 0x00);\n+\t\tmutex_unlock(&hub->page_mutex);\n+\t}\n+\n+\treturn ret;\n+unlock:\n+\tregmap_write(hub->regmap, I3C_HUB_PAGE_PTR, 0x00);\n+\tmutex_unlock(&hub->page_mutex);\n+\treturn ret;\n+}\n+\n+static inline bool i3c_hub_can_combine_wr_rd(const struct i2c_msg *w,\n+\t\t\t\t\t const struct i2c_msg *r)\n+{\n+\t/* w: write, r: read; same addr; total length within payload */\n+\treturn !(w->flags & I2C_M_RD) && (r->flags & I2C_M_RD) &&\n+\t w->addr == r->addr &&\n+\t (w->len + r->len) <= I3C_HUB_SMBUS_PAYLOAD_SIZE;\n+}\n+\n+/**\n+ * i3c_hub_smbus_port_adapter_xfer() - i3c hub smbus transfer logic\n+ * @adap: i2c_adapter corresponding with single port in the i3c hub\n+ * @xfers: all messages descriptors and data\n+ * @nxfers: amount of single messages in a transfer\n+ *\n+ * Return: function returns the sum of correctly sent messages (only those with hub return\n+ * status 0x01)\n+ */\n+static int i3c_hub_smbus_port_adapter_xfer(struct i2c_adapter *adap,\n+\t\t\t\t\t struct i2c_msg *xfers, int nxfers)\n+{\n+\tstruct i2c_adapter_group *smbus = i2c_get_adapdata(adap);\n+\tstruct i3c_hub *hub = smbus->priv;\n+\tint ret_sum = 0, ret, len, type, nxfers_i;\n+\tconst struct i2c_msg *cur = NULL, *next = NULL;\n+\n+\tfor (nxfers_i = 0; nxfers_i < nxfers; nxfers_i++) {\n+\t\tcur = &xfers[nxfers_i];\n+\t\tlen = cur->len;\n+\t\ttype = cur->flags & I2C_M_RD ? I3C_HUB_SMBUS_XFER_READ :\n+\t\t\t\t\t I3C_HUB_SMBUS_XFER_WRITE;\n+\n+\t\t/* Per-message length limit check */\n+\t\tif (len > I3C_HUB_SMBUS_PAYLOAD_SIZE) {\n+\t\t\tdev_err(&adap->dev,\n+\t\t\t\t\"Message nr. %d not sent - length over %d bytes.\\n\",\n+\t\t\t\tnxfers_i, I3C_HUB_SMBUS_PAYLOAD_SIZE);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* Try to combine write followed by read to the same address */\n+\t\tif (type == I3C_HUB_SMBUS_XFER_WRITE &&\n+\t\t (nxfers_i + 1) < nxfers) {\n+\t\t\tnext = &xfers[nxfers_i + 1];\n+\t\t\tif (i3c_hub_can_combine_wr_rd(cur, next))\n+\t\t\t\ttype = I3C_HUB_SMBUS_XFER_WR_RD;\n+\t\t}\n+\n+\t\tret = i3c_hub_smbus_msg(hub, xfers, smbus->tp_port, nxfers_i,\n+\t\t\t\t\ttype);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (type == I3C_HUB_SMBUS_XFER_WR_RD) {\n+\t\t\tret_sum += 2;\n+\t\t\tnxfers_i++; /* skip the next read message */\n+\n+\t\t} else {\n+\t\t\tret_sum++;\n+\t\t}\n+\t}\n+\treturn ret_sum;\n+}\n+\n+static int i3c_hub_bus_init(struct i3c_master_controller *controller)\n+{\n+\tstruct logical_bus *bus =\n+\t\tcontainer_of(controller, struct logical_bus, controller);\n+\n+\tcontroller->this = bus->priv->i3cdev->desc;\n+\treturn 0;\n+}\n+\n+static void i3c_hub_bus_cleanup(struct i3c_master_controller *controller)\n+{\n+\tcontroller->this = NULL;\n+}\n+\n+static int i3c_hub_attach_i3c_dev(struct i3c_dev_desc *dev)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i3c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\tint ret;\n+\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tret = parent->ops->attach_i3c_dev(dev);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+\treturn ret;\n+}\n+\n+static int i3c_hub_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i3c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\tint ret;\n+\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tret = parent->ops->reattach_i3c_dev(dev, old_dyn_addr);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+\treturn ret;\n+}\n+\n+static void i3c_hub_detach_i3c_dev(struct i3c_dev_desc *dev)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i3c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tparent->ops->detach_i3c_dev(dev);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+}\n+\n+static int i3c_hub_do_daa(struct i3c_master_controller *controller)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_from_controller(controller);\n+\tint ret;\n+\n+\tdown_write(&parent->bus.lock);\n+\tret = parent->ops->do_daa(parent);\n+\tup_write(&parent->bus.lock);\n+\treturn ret;\n+}\n+\n+static bool i3c_hub_supports_ccc_cmd(struct i3c_master_controller *controller,\n+\t\t\t\t const struct i3c_ccc_cmd *cmd)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_from_controller(controller);\n+\n+\treturn parent->ops->supports_ccc_cmd(parent, cmd);\n+}\n+\n+static int i3c_hub_send_ccc_cmd(struct i3c_master_controller *controller,\n+\t\t\t\tstruct i3c_ccc_cmd *cmd)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_from_controller(controller);\n+\tstruct logical_bus *bus =\n+\t\tcontainer_of(controller, struct logical_bus, controller);\n+\tint ret;\n+\n+\tif (cmd->id == I3C_CCC_RSTDAA(true))\n+\t\treturn 0;\n+\n+\ti3c_hub_trans_pre_cb(bus);\n+\tdown_read(&parent->bus.lock);\n+\tret = parent->ops->send_ccc_cmd(parent, cmd);\n+\tup_read(&parent->bus.lock);\n+\ti3c_hub_trans_post_cb(bus);\n+\n+\treturn ret;\n+}\n+\n+static int i3c_hub_priv_xfers(struct i3c_dev_desc *dev,\n+\t\t\t struct i3c_xfer *xfers, int nxfers,\n+\t\t\t enum i3c_xfer_mode mode)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i3c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\tstruct logical_bus *bus = bus_from_i3c_desc(dev);\n+\tint res;\n+\n+\ti3c_hub_trans_pre_cb(bus);\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tdown_read(&parent->bus.lock);\n+\tres = parent->ops->i3c_xfers(dev, xfers, nxfers, mode);\n+\tup_read(&parent->bus.lock);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+\ti3c_hub_trans_post_cb(bus);\n+\n+\treturn res;\n+}\n+\n+static int i3c_hub_attach_i2c_dev(struct i2c_dev_desc *dev)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i2c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\tint ret;\n+\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tret = parent->ops->attach_i2c_dev(dev);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+\treturn ret;\n+}\n+\n+static void i3c_hub_detach_i2c_dev(struct i2c_dev_desc *dev)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i2c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tparent->ops->detach_i2c_dev(dev);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+}\n+\n+static int i3c_hub_i2c_xfers(struct i2c_dev_desc *dev, struct i2c_msg *xfers,\n+\t\t\t int nxfers)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i2c_desc(dev);\n+\tstruct logical_bus *bus = bus_from_i2c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\tint ret;\n+\n+\ti3c_hub_trans_pre_cb(bus);\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tret = parent->ops->i2c_xfers(dev, xfers, nxfers);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+\ti3c_hub_trans_post_cb(bus);\n+\treturn ret;\n+}\n+\n+static int i3c_hub_request_ibi(struct i3c_dev_desc *dev,\n+\t\t\t const struct i3c_ibi_setup *req)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i3c_desc(dev);\n+\tstruct logical_bus *bus = bus_from_i3c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\tint ret;\n+\n+\ti3c_hub_trans_pre_cb(bus);\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tdown_read(&parent->bus.lock);\n+\tret = parent->ops->request_ibi(dev, req);\n+\tup_read(&parent->bus.lock);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+\ti3c_hub_trans_post_cb(bus);\n+\treturn ret;\n+}\n+\n+static void i3c_hub_free_ibi(struct i3c_dev_desc *dev)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i3c_desc(dev);\n+\tstruct logical_bus *bus = bus_from_i3c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\n+\ti3c_hub_trans_pre_cb(bus);\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tdown_read(&parent->bus.lock);\n+\tparent->ops->free_ibi(dev);\n+\tup_read(&parent->bus.lock);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+\ti3c_hub_trans_post_cb(bus);\n+}\n+\n+static int i3c_hub_enable_ibi(struct i3c_dev_desc *dev)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i3c_desc(dev);\n+\tstruct logical_bus *bus = bus_from_i3c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\tint ret;\n+\n+\ti3c_hub_trans_pre_cb(bus);\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tdown_read(&parent->bus.lock);\n+\tret = parent->ops->enable_ibi(dev);\n+\tup_read(&parent->bus.lock);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+\ti3c_hub_trans_post_cb(bus);\n+\treturn ret;\n+}\n+\n+static int i3c_hub_disable_ibi(struct i3c_dev_desc *dev)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i3c_desc(dev);\n+\tstruct logical_bus *bus = bus_from_i3c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\tint ret;\n+\n+\ti3c_hub_trans_pre_cb(bus);\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tdown_read(&parent->bus.lock);\n+\tret = parent->ops->disable_ibi(dev);\n+\tup_read(&parent->bus.lock);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+\ti3c_hub_trans_post_cb(bus);\n+\treturn ret;\n+}\n+\n+static void i3c_hub_recycle_ibi_slot(struct i3c_dev_desc *dev,\n+\t\t\t\t struct i3c_ibi_slot *slot)\n+{\n+\tstruct i3c_master_controller *parent =\n+\t\tparent_controller_from_i3c_desc(dev);\n+\tstruct i3c_master_controller *orig_parent;\n+\n+\torig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);\n+\tparent->ops->recycle_ibi_slot(dev, slot);\n+\trestore_i3c_i2c_desc_parent(&dev->common, orig_parent);\n+}\n+\n+static const struct i3c_master_controller_ops i3c_hub_i3c_ops = {\n+\t.bus_init = i3c_hub_bus_init,\n+\t.bus_cleanup = i3c_hub_bus_cleanup,\n+\t.attach_i3c_dev = i3c_hub_attach_i3c_dev,\n+\t.reattach_i3c_dev = i3c_hub_reattach_i3c_dev,\n+\t.detach_i3c_dev = i3c_hub_detach_i3c_dev,\n+\t.do_daa = i3c_hub_do_daa,\n+\t.supports_ccc_cmd = i3c_hub_supports_ccc_cmd,\n+\t.send_ccc_cmd = i3c_hub_send_ccc_cmd,\n+\t.i3c_xfers = i3c_hub_priv_xfers,\n+\t.attach_i2c_dev = i3c_hub_attach_i2c_dev,\n+\t.detach_i2c_dev = i3c_hub_detach_i2c_dev,\n+\t.i2c_xfers = i3c_hub_i2c_xfers,\n+\t.request_ibi = i3c_hub_request_ibi,\n+\t.free_ibi = i3c_hub_free_ibi,\n+\t.enable_ibi = i3c_hub_enable_ibi,\n+\t.disable_ibi = i3c_hub_disable_ibi,\n+\t.recycle_ibi_slot = i3c_hub_recycle_ibi_slot,\n+};\n+\n+static int i3c_hub_logic_register(struct i3c_master_controller *controller,\n+\t\t\t\t struct device *parent)\n+{\n+\treturn i3c_master_register(controller, parent, &i3c_hub_i3c_ops, false);\n+}\n+\n+static u32 i3c_hub_smbus_funcs(struct i2c_adapter *adapter)\n+{\n+\treturn (I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C) & ~I2C_FUNC_SMBUS_QUICK;\n+}\n+\n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+static int reg_i2c_target(struct i2c_client *client)\n+{\n+\tstruct i2c_adapter_group *smbus = i2c_get_adapdata(client->adapter);\n+\tstruct smbus_backend *backend;\n+\tint ret = 0;\n+\n+\tif (!smbus)\n+\t\treturn -EINVAL;\n+\n+\tmutex_lock(&smbus->mutex);\n+\n+\tlist_for_each_entry(backend, &smbus->backend_entry, list) {\n+\t\tif (backend->client->addr == client->addr) {\n+\t\t\tret = -EBUSY;\n+\t\t\tgoto out;\n+\t\t}\n+\t}\n+\n+\tbackend = kzalloc(sizeof(*backend), GFP_KERNEL);\n+\tif (!backend) {\n+\t\tret = -ENOMEM;\n+\t\tgoto out;\n+\t}\n+\n+\tbackend->client = client;\n+\tlist_add(&backend->list, &smbus->backend_entry);\n+\n+out:\n+\tmutex_unlock(&smbus->mutex);\n+\treturn ret;\n+}\n+\n+static int unreg_i2c_target(struct i2c_client *client)\n+{\n+\tstruct i2c_adapter_group *smbus = i2c_get_adapdata(client->adapter);\n+\tstruct smbus_backend *backend;\n+\tbool found = false;\n+\n+\tif (!smbus)\n+\t\treturn -EINVAL;\n+\n+\tmutex_lock(&smbus->mutex);\n+\n+\tlist_for_each_entry(backend, &smbus->backend_entry, list) {\n+\t\tif (backend->client->addr == client->addr) {\n+\t\t\tlist_del(&backend->list);\n+\t\t\tkfree(backend);\n+\t\t\tfound = true;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tmutex_unlock(&smbus->mutex);\n+\treturn found ? 0 : -ENODEV;\n+}\n+#endif /* CONFIG_I2C_SLAVE */\n+\n+static const struct i2c_algorithm i3c_hub_smbus_algo = {\n+\t.master_xfer = i3c_hub_smbus_port_adapter_xfer,\n+\t.functionality = i3c_hub_smbus_funcs,\n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\t.reg_slave = reg_i2c_target,\n+\t.unreg_slave = unreg_i2c_target,\n+#endif\n+};\n+\n+static void i3c_hub_delayed_work(struct work_struct *work)\n+{\n+\tstruct i3c_hub *priv =\n+\t\tcontainer_of(work, typeof(*priv), delayed_work.work);\n+\tstruct device *dev = i3cdev_to_dev(priv->i3cdev);\n+\tstruct logical_bus *bus;\n+\tstruct i2c_adapter_group *smbus;\n+\tint ret;\n+\tint i;\n+\tunsigned int reg_val = 0;\n+\n+\t/* record reg 81: tp hubnetwork connection setting */\n+\tret = regmap_read(priv->regmap, I3C_HUB_TP_NET_CON_CONF, ®_val);\n+\tif (ret) {\n+\t\tdev_warn(dev, \"Failed to read hubnetwork connection setting\\n\");\n+\t\treturn;\n+\t}\n+\n+\tret = regmap_write(priv->regmap, I3C_HUB_TP_NET_CON_CONF, 0x00);\n+\tif (ret) {\n+\t\tdev_warn(dev, \"Failed to close Target Port(s)\\n\");\n+\t\treturn;\n+\t}\n+\n+\tfor (i = 0; i < priv->dev_info->n_ports; ++i) {\n+\t\tbus = &priv->logical_bus[i];\n+\t\tif (bus->available) {\n+\t\t\tret = regmap_update_bits(\n+\t\t\t\tpriv->regmap, I3C_HUB_TP_NET_CON_CONF,\n+\t\t\t\tGENMASK(bus->tp_id, bus->tp_id), bus->tp_map);\n+\t\t\tif (ret) {\n+\t\t\t\tdev_warn(dev,\n+\t\t\t\t\t \"Failed to open Target Port(s)\\n\");\n+\t\t\t\treturn;\n+\t\t\t}\n+\n+\t\t\tdev->of_node = bus->of_node;\n+\t\t\tret = i3c_hub_logic_register(&bus->controller, dev);\n+\t\t\tif (ret) {\n+\t\t\t\tdev_warn(dev,\n+\t\t\t\t\t \"Failed to register i3c controller - bus id:%i\\n\",\n+\t\t\t\t\t i);\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t\tbus->registered = true;\n+\n+\t\t\tret = regmap_update_bits(\n+\t\t\t\tpriv->regmap, I3C_HUB_TP_NET_CON_CONF,\n+\t\t\t\tGENMASK(bus->tp_id, bus->tp_id), 0x00);\n+\t\t\tif (ret) {\n+\t\t\t\tdev_warn(dev,\n+\t\t\t\t\t \"Failed to close Target Port(s)\\n\");\n+\t\t\t\treturn;\n+\t\t\t}\n+\n+\t\t\tif (!priv->settings.tp[i].always_enable)\n+\t\t\t\treg_val &= ~GENMASK(bus->tp_id, bus->tp_id);\n+\t\t}\n+\t}\n+\n+\t/* update tp hubnetwork connection setting */\n+\tret = regmap_write(priv->regmap, I3C_HUB_TP_NET_CON_CONF, reg_val);\n+\tif (ret) {\n+\t\tdev_warn(dev, \"Failed to open Target Port(s)\\n\");\n+\t\treturn;\n+\t}\n+\n+\tret = i3c_master_do_daa(priv->driving_master);\n+\tif (ret) {\n+\t\tdev_warn(dev, \"Failed to run DAA\\n\");\n+\t\treturn;\n+\t}\n+\n+\tfor (i = 0; i < priv->dev_info->n_ports; i++) {\n+\t\tsmbus = &priv->smbus_port_adapter[i];\n+\t\tif (!smbus->used)\n+\t\t\tcontinue;\n+\n+\t\tif (!priv->settings.tp[i].poll_interval_ms)\n+\t\t\tcontinue;\n+\n+\t\tschedule_delayed_work(\n+\t\t\t&smbus->delayed_work_polling,\n+\t\t\tmsecs_to_jiffies(\n+\t\t\t\tpriv->settings.tp[i].poll_interval_ms));\n+\t}\n+}\n+\n+static int send_smbus_target_data_to_backend(struct i2c_adapter_group *smbus,\n+\t\t\t\t\t u8 address, u8 *local_buffer,\n+\t\t\t\t\t u8 len)\n+{\n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\tstruct smbus_backend *backend;\n+\tstruct i2c_client *client;\n+\tint i, ret;\n+\tu8 tmp;\n+\n+\tmutex_lock(&smbus->mutex);\n+\n+\tlist_for_each_entry(backend, &smbus->backend_entry, list) {\n+\t\tclient = backend->client;\n+\t\tif (client->addr == address >> 1) {\n+\t\t\tmutex_unlock(&smbus->mutex);\n+\t\t\tret = i2c_slave_event(client, I2C_SLAVE_WRITE_REQUESTED,\n+\t\t\t\t\t &address);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\n+\t\t\tfor (i = 0; i < len; i++) {\n+\t\t\t\tret = i2c_slave_event(client,\n+\t\t\t\t\t\t I2C_SLAVE_WRITE_RECEIVED,\n+\t\t\t\t\t\t &local_buffer[i]);\n+\t\t\t\tif (ret)\n+\t\t\t\t\treturn ret;\n+\t\t\t}\n+\n+\t\t\treturn i2c_slave_event(client, I2C_SLAVE_STOP, &tmp);\n+\t\t}\n+\t}\n+\n+\tmutex_unlock(&smbus->mutex);\n+#endif /* CONFIG_I2C_SLAVE */\n+\treturn -ENXIO;\n+}\n+\n+static int read_smbus_target_buffer_page(struct i2c_adapter_group *smbus,\n+\t\t\t\t\t u8 target_buffer_page, u8 *address,\n+\t\t\t\t\t u8 *local_buffer, u8 *len)\n+{\n+\tstruct i3c_hub *hub = smbus->priv;\n+\tstruct device *dev = i3cdev_to_dev(hub->i3cdev);\n+\tu32 status;\n+\tint ret;\n+\n+\tmutex_lock(&hub->page_mutex);\n+\tregmap_write(hub->regmap, I3C_HUB_PAGE_PTR, target_buffer_page);\n+\n+\tret = regmap_read(hub->regmap, I3C_HUB_TARGET_BUFF_LENGTH, &status);\n+\tif (ret)\n+\t\tgoto error;\n+\n+\t*len = status - 1;\n+\tif (!*len)\n+\t\tgoto error;\n+\n+\tif (*len > I3C_HUB_SMBUS_TARGET_PAYLOAD_SIZE) {\n+\t\tdev_warn_ratelimited(\n+\t\t\tdev, \"Received message too big for hub buffer\\n\");\n+\t\tret = -EMSGSIZE;\n+\t\t*len = 0;\n+\t\tgoto error;\n+\t}\n+\n+\tret = regmap_read(hub->regmap, I3C_HUB_TARGET_BUFF_ADDRESS, &status);\n+\tif (ret)\n+\t\tgoto error;\n+\n+\t*address = status;\n+\n+\tret = regmap_bulk_read(hub->regmap, I3C_HUB_TARGET_BUFF_DATA,\n+\t\t\t local_buffer, *len);\n+\n+error:\n+\tregmap_write(hub->regmap, I3C_HUB_PAGE_PTR, 0x00);\n+\tmutex_unlock(&hub->page_mutex);\n+\treturn ret;\n+}\n+\n+static int process_smbus_controller_status(struct i2c_adapter_group *smbus,\n+\t\t\t\t\t u8 reg, u32 status)\n+{\n+\tstruct i3c_hub *hub = smbus->priv;\n+\tint ret = 0;\n+\n+\tif (status & I3C_HUB_CONTROLLER_AGENT_FINISH_FLAG) {\n+\t\tsmbus->status = status;\n+\t\tret = regmap_write(hub->regmap, reg,\n+\t\t\t\t I3C_HUB_CONTROLLER_AGENT_FINISH_FLAG);\n+\t\tcomplete(&smbus->completion);\n+\t}\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * Controller buffer page is determined by adding the first buffer page number to port\n+ * index multiplied by four. The two target buffer page numbers are determined the same\n+ * way but they are offset by 2 and 3 from the controller page.\n+ */\n+static int process_smbus_target_status(struct i2c_adapter_group *smbus, u8 reg,\n+\t\t\t\t u32 status)\n+{\n+\tstruct i3c_hub *hub = smbus->priv;\n+\tstruct device *dev = i3cdev_to_dev(hub->i3cdev);\n+\tu8 controller_buffer_page =\n+\t\tI3C_HUB_CONTROLLER_BUFFER_PAGE + 4 * smbus->tp_port;\n+\tu8 local_buffer[I3C_HUB_SMBUS_TARGET_PAYLOAD_SIZE] = { 0 };\n+\tu8 target_buffer_page, address = 0, len = 0, flag;\n+\tint ret;\n+\n+\tif (smbus->last_processed_buf)\n+\t\tstatus &= ~smbus->last_processed_buf;\n+\n+\tif (status & I3C_HUB_TARGET_BUF_0_RECEIVE) {\n+\t\ttarget_buffer_page = controller_buffer_page + 2;\n+\t\tflag = I3C_HUB_TARGET_BUF_0_RECEIVE;\n+\t} else if (status & I3C_HUB_TARGET_BUF_1_RECEIVE) {\n+\t\ttarget_buffer_page = controller_buffer_page + 3;\n+\t\tflag = I3C_HUB_TARGET_BUF_1_RECEIVE;\n+\t} else {\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tret = read_smbus_target_buffer_page(smbus, target_buffer_page, &address,\n+\t\t\t\t\t local_buffer, &len);\n+\tif (ret && ret != -EMSGSIZE) {\n+\t\tdev_dbg(dev, \"Failed to read target buffer page: %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\tsmbus->last_processed_buf = flag;\n+\n+\tif (status & I3C_HUB_TARGET_BUF_OVRFL)\n+\t\tflag |= I3C_HUB_TARGET_BUF_OVRFL;\n+\n+\tret = regmap_write(hub->regmap, reg, flag);\n+\tif (ret) {\n+\t\tdev_dbg(dev, \"Failed to clear target port status\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tif (len) {\n+\t\tret = send_smbus_target_data_to_backend(smbus, address,\n+\t\t\t\t\t\t\tlocal_buffer, len);\n+\t\tif (ret) {\n+\t\t\tdev_dbg(dev, \"Failed to send data to backend: %d\\n\",\n+\t\t\t\tret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int i3c_hub_process_smbus_status(struct i2c_adapter_group *smbus)\n+{\n+\tstruct i3c_hub *hub = smbus->priv;\n+\tu8 target_port_status = I3C_HUB_TP0_SMBUS_AGNT_STS + smbus->tp_port;\n+\tstruct device *dev = i3cdev_to_dev(hub->i3cdev);\n+\tu32 status;\n+\tint ret = 0;\n+\tu32 poll_interval_ms =\n+\t\thub->settings.tp[smbus->tp_port].poll_interval_ms;\n+\n+\tret = regmap_read(hub->regmap, target_port_status, &status);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* smbus controller agent status */\n+\tif (!poll_interval_ms) {\n+\t\tret = process_smbus_controller_status(smbus, target_port_status,\n+\t\t\t\t\t\t status);\n+\t\tif (ret)\n+\t\t\tdev_warn_ratelimited(\n+\t\t\t\tdev,\n+\t\t\t\t\"Failed to process smbus controller status\\n\");\n+\t}\n+\n+\t/* smbus target agent status */\n+\tstatus &= I3C_HUB_TARGET_BUF_STATUS_MASK;\n+\n+\twhile (status) {\n+\t\tret = process_smbus_target_status(smbus, target_port_status,\n+\t\t\t\t\t\t status);\n+\t\tif (ret) {\n+\t\t\tdev_warn_ratelimited(\n+\t\t\t\tdev,\n+\t\t\t\t\"Failed to process smbus target status: %d\\n\",\n+\t\t\t\tret);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (!poll_interval_ms)\n+\t\t\tbreak;\n+\n+\t\tret = regmap_read(hub->regmap, target_port_status, &status);\n+\t\tif (ret)\n+\t\t\tbreak;\n+\t\tstatus &= I3C_HUB_TARGET_BUF_STATUS_MASK;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * i3c_hub_delayed_work_polling() - This delayed work is a polling mechanism to\n+ * find if any transaction happened.\n+ */\n+static void i3c_hub_delayed_work_polling(struct work_struct *work)\n+{\n+\tstruct i2c_adapter_group *smbus =\n+\t\tcontainer_of(work, typeof(*smbus), delayed_work_polling.work);\n+\tstruct device *dev = i3cdev_to_dev(smbus->priv->i3cdev);\n+\tint ret;\n+\n+\tif (!list_empty(&smbus->backend_entry)) {\n+\t\tret = i3c_hub_process_smbus_status(smbus);\n+\t\tif (ret)\n+\t\t\tdev_warn_ratelimited(\n+\t\t\t\tdev,\n+\t\t\t\t\"Failed to process TP %u smbus status: %d\\n\",\n+\t\t\t\tsmbus->tp_port, ret);\n+\t}\n+\n+\tschedule_delayed_work(\n+\t\t&smbus->delayed_work_polling,\n+\t\tmsecs_to_jiffies(smbus->priv->settings.tp[smbus->tp_port]\n+\t\t\t\t\t .poll_interval_ms));\n+}\n+\n+static int i3c_hub_smbus_ibi_handler(struct i3c_hub *hub,\n+\t\t\t\t const struct i3c_ibi_payload *payload)\n+{\n+\tstruct i2c_adapter_group *smbus;\n+\tu8 tp, tps;\n+\tint val, ret = 0, rc;\n+\tstruct device *dev = i3cdev_to_dev(hub->i3cdev);\n+\n+\tif (payload->len < 2) {\n+\t\tret = regmap_read(hub->regmap, I3C_HUB_TP_SMBUS_AGNT_IBI_STS,\n+\t\t\t\t &val);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\ttps = (u8)val;\n+\t} else {\n+\t\ttps = ((const u8 *)payload->data)[1];\n+\t}\n+\n+\tif (!tps)\n+\t\treturn 0;\n+\n+\twhile (tps) {\n+\t\ttp = (u8)__ffs((unsigned long)tps);\n+\t\ttps &= (tps - 1);\n+\n+\t\tif (hub->settings.tp[tp].poll_interval_ms)\n+\t\t\tcontinue;\n+\n+\t\tsmbus = &hub->smbus_port_adapter[tp];\n+\t\trc = i3c_hub_process_smbus_status(smbus);\n+\t\tif (rc) {\n+\t\t\tdev_warn_ratelimited(\n+\t\t\t\tdev,\n+\t\t\t\t\"Failed to process TP %u smbus status: %d\\n\",\n+\t\t\t\ttp, rc);\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Sysfs attribute: clock_frequency\n+ * Read/Write the SMBus clock frequency for this adapter's port.\n+ */\n+static ssize_t clock_frequency_show(struct device *dev,\n+\t\t\t\t struct device_attribute *attr, char *buf)\n+{\n+\tstruct i2c_adapter *adap = to_i2c_adapter(dev);\n+\tstruct i2c_adapter_group *smbus = i2c_get_adapdata(adap);\n+\tstruct i3c_hub *hub = smbus->priv;\n+\n+\treturn sprintf(buf, \"%u\\n\",\n+\t\t hub->settings.tp[smbus->tp_port].clock_frequency);\n+}\n+\n+static ssize_t clock_frequency_store(struct device *dev,\n+\t\t\t\t struct device_attribute *attr,\n+\t\t\t\t const char *buf, size_t count)\n+{\n+\tstruct i2c_adapter *adap = to_i2c_adapter(dev);\n+\tstruct i2c_adapter_group *smbus = i2c_get_adapdata(adap);\n+\tstruct i3c_hub *hub = smbus->priv;\n+\tu32 val;\n+\tint ret;\n+\n+\tret = kstrtou32(buf, 0, &val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (!i3c_hub_smbus_validate_clock_frequency(val))\n+\t\treturn -EINVAL;\n+\n+\thub->settings.tp[smbus->tp_port].clock_frequency = val;\n+\n+\treturn count;\n+}\n+static DEVICE_ATTR_RW(clock_frequency);\n+\n+static struct attribute *i3c_hub_smbus_attrs[] = {\n+\t&dev_attr_clock_frequency.attr,\n+\tNULL,\n+};\n+\n+ATTRIBUTE_GROUPS(i3c_hub_smbus);\n+\n+static int i3c_hub_smbus_tp_algo(struct i3c_hub *priv, int i)\n+{\n+\tstruct device *dev = i3cdev_to_dev(priv->i3cdev);\n+\tint ret;\n+\tstruct i2c_adapter_group *smbus = &priv->smbus_port_adapter[i];\n+\tstruct i2c_adapter *i2c = &smbus->i2c;\n+\n+\tmutex_init(&smbus->mutex);\n+\tINIT_LIST_HEAD(&smbus->backend_entry);\n+\tsmbus->priv = priv;\n+\tsmbus->tp_port = i;\n+\tsmbus->tp_mask = BIT(i);\n+\n+\tinit_completion(&smbus->completion);\n+\ti2c->owner = THIS_MODULE;\n+\ti2c->algo = &i3c_hub_smbus_algo;\n+\ti2c->dev.parent = dev;\n+\ti2c->dev.of_node = smbus->of_node;\n+\ti2c->dev.groups = i3c_hub_smbus_groups;\n+\ti2c->timeout = HZ;\n+\ti2c->retries = 3;\n+\tsnprintf(i2c->name, sizeof(i2c->name), \"hub%s.port%d\", dev_name(dev),\n+\t\t smbus->tp_port);\n+\n+\tif (priv->settings.tp[i].poll_interval_ms) {\n+\t\tret = regmap_clear_bits(priv->regmap, I3C_HUB_TP_IBI_CONF,\n+\t\t\t\t\tsmbus->tp_mask);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tINIT_DELAYED_WORK(&smbus->delayed_work_polling,\n+\t\t\t\t i3c_hub_delayed_work_polling);\n+\t\tpriv->smbus_ibi_en_mask &= ~smbus->tp_mask;\n+\t} else {\n+\t\tret = regmap_set_bits(priv->regmap, I3C_HUB_TP_IBI_CONF,\n+\t\t\t\t smbus->tp_mask);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tpriv->smbus_ibi_en_mask |= smbus->tp_mask;\n+\t}\n+\n+\t/* Enable SDA hold-low when both SMBus Target Agent buffers are full.\n+\t * Used as a flow-control mechanism for MCTP to avoid upstream TX timeout\n+\t * when target buffers are not serviced in time.\n+\t */\n+\tret = regmap_set_bits(priv->regmap,\n+\t\t\t I3C_HUB_ONCHIP_TD_AND_SMBUS_AGNT_CONF,\n+\t\t\t TARGET_AGENT_BUF_FULL_SDA_LOW_EN);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\ti2c_set_adapdata(i2c, smbus);\n+\n+\tret = i2c_add_adapter(i2c);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tsmbus->used = 1;\n+\treturn ret;\n+}\n+\n+static int i3c_hub_gpio_direction_input(struct gpio_chip *gc, unsigned int off)\n+{\n+\tstruct i3c_hub *hub = gpiochip_get_data(gc);\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\tint ret = 0;\n+\tu8 reg, mask = 0;\n+\n+\treg = off % GPIO_BANK_SZ ? I3C_HUB_TP_SDA_OUT_EN :\n+\t\t\t\t I3C_HUB_TP_SCL_OUT_EN;\n+\tmask = BIT(gpio->tp[off / GPIO_BANK_SZ]);\n+\n+\tret = regmap_update_bits(hub->regmap, reg, mask, 0);\n+\treturn ret;\n+}\n+\n+static int i3c_hub_gpio_direction_output(struct gpio_chip *gc, unsigned int off,\n+\t\t\t\t\t int val)\n+{\n+\tstruct i3c_hub *hub = gpiochip_get_data(gc);\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\tint ret = 0;\n+\tu8 reg, mask = 0;\n+\n+\treg = off % GPIO_BANK_SZ ? I3C_HUB_TP_SDA_OUT_EN :\n+\t\t\t\t I3C_HUB_TP_SCL_OUT_EN;\n+\tmask = BIT(gpio->tp[off / GPIO_BANK_SZ]);\n+\n+\tret = regmap_update_bits(hub->regmap, reg, mask, mask);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = regmap_update_bits(hub->regmap, reg + 2, mask, val ? mask : 0);\n+\treturn ret;\n+}\n+\n+static int i3c_hub_gpio_get_value(struct gpio_chip *gc, unsigned int off)\n+{\n+\tstruct i3c_hub *hub = gpiochip_get_data(gc);\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\tint ret = 0, val = 0, dir;\n+\tu8 reg, shift = 0;\n+\n+\tdir = gc->get_direction(gc, off);\n+\tif (dir)\n+\t\treg = off % GPIO_BANK_SZ ? I3C_HUB_TP_SDA_IN_LEVEL_STS :\n+\t\t\t\t\t I3C_HUB_TP_SCL_IN_LEVEL_STS;\n+\telse\n+\t\treg = off % GPIO_BANK_SZ ? I3C_HUB_TP_SDA_OUT_LEVEL :\n+\t\t\t\t\t I3C_HUB_TP_SCL_OUT_LEVEL;\n+\n+\tshift = gpio->tp[off / GPIO_BANK_SZ];\n+\n+\tret = regmap_read(hub->regmap, reg, &val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = (val >> shift) & 0x01;\n+\treturn ret;\n+}\n+\n+static int i3c_hub_gpio_set_value(struct gpio_chip *gc, unsigned int off, int val)\n+{\n+\tstruct i3c_hub *hub = gpiochip_get_data(gc);\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\tu8 reg, mask = 0;\n+\n+\treg = off % GPIO_BANK_SZ ? I3C_HUB_TP_SDA_OUT_LEVEL :\n+\t\t\t\t I3C_HUB_TP_SCL_OUT_LEVEL;\n+\tmask = BIT(gpio->tp[off / GPIO_BANK_SZ]);\n+\n+\treturn regmap_update_bits(hub->regmap, reg, mask, val ? mask : 0);\n+}\n+\n+static int i3c_hub_gpio_get_direction(struct gpio_chip *gc, unsigned int off)\n+{\n+\tstruct i3c_hub *hub = gpiochip_get_data(gc);\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\tint ret = 0, dir = 0;\n+\tu8 reg, shift = 0;\n+\n+\treg = off % GPIO_BANK_SZ ? I3C_HUB_TP_SDA_OUT_EN :\n+\t\t\t\t I3C_HUB_TP_SCL_OUT_EN;\n+\tshift = gpio->tp[off / GPIO_BANK_SZ];\n+\n+\tret = regmap_read(hub->regmap, reg, &dir);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = ~(dir >> shift) & 0x01;\n+\treturn ret;\n+}\n+\n+static void i3c_hub_gpio_irq_mask(struct irq_data *d)\n+{\n+\tstruct gpio_chip *gc = irq_data_get_irq_chip_data(d);\n+\tstruct i3c_hub *hub = gpiochip_get_data(gc);\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\tu8 reg, hwirq = 0, mask = 0;\n+\n+\thwirq = irqd_to_hwirq(d);\n+\n+\treg = hwirq % GPIO_BANK_SZ ? I3C_HUB_TP_SDA_IN_DETECT_IBI_EN :\n+\t\t\t\t I3C_HUB_TP_SCL_IN_DETECT_IBI_EN;\n+\tmask = BIT(gpio->tp[hwirq / GPIO_BANK_SZ]);\n+\n+\tregmap_update_bits(hub->regmap, reg, mask, 0);\n+}\n+\n+static void i3c_hub_gpio_irq_unmask(struct irq_data *d)\n+{\n+\tstruct gpio_chip *gc = irq_data_get_irq_chip_data(d);\n+\tstruct i3c_hub *hub = gpiochip_get_data(gc);\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\tu8 reg, hwirq = 0, mask = 0;\n+\n+\thwirq = irqd_to_hwirq(d);\n+\n+\treg = hwirq % GPIO_BANK_SZ ? I3C_HUB_TP_SDA_IN_DETECT_IBI_EN :\n+\t\t\t\t I3C_HUB_TP_SCL_IN_DETECT_IBI_EN;\n+\tmask = BIT(gpio->tp[hwirq / GPIO_BANK_SZ]);\n+\n+\tregmap_update_bits(hub->regmap, reg, mask, mask);\n+}\n+\n+static int i3c_hub_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)\n+{\n+\tstruct gpio_chip *gc = irq_data_get_irq_chip_data(d);\n+\tstruct i3c_hub *hub = gpiochip_get_data(gc);\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\tu8 hwirq = 0, mask = 0, val, tp, reg;\n+\tint ret;\n+\n+\tif (!(flow_type & IRQ_TYPE_EDGE_BOTH)) {\n+\t\tdev_err(&hub->i3cdev->dev, \"irq %d: unsupported type %d\\n\",\n+\t\t\td->irq, flow_type);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\thwirq = irqd_to_hwirq(d);\n+\ttp = gpio->tp[hwirq / GPIO_BANK_SZ];\n+\n+\tif (tp == 0 || tp == 1 || tp == 4 || tp == 5) {\n+\t\tif (hwirq % GPIO_BANK_SZ) {\n+\t\t\tmask = SDA0145_IO_IN_DET_CFG_MASK;\n+\t\t\tval = SDA0145_IO_IN_DET_CFG(flow_type);\n+\t\t\treg = I3C_HUB_TP_SDA_IN_DETECT_FLG;\n+\t\t} else {\n+\t\t\tmask = SCL0145_IO_IN_DET_CFG_MASK;\n+\t\t\tval = SCL0145_IO_IN_DET_CFG(flow_type);\n+\t\t\treg = I3C_HUB_TP_SCL_IN_DETECT_FLG;\n+\t\t}\n+\t} else {\n+\t\tif (hwirq % GPIO_BANK_SZ) {\n+\t\t\tmask = SDA2367_IO_IN_DET_CFG_MASK;\n+\t\t\tval = SDA2367_IO_IN_DET_CFG(flow_type);\n+\t\t\treg = I3C_HUB_TP_SDA_IN_DETECT_FLG;\n+\t\t} else {\n+\t\t\tmask = SCL2367_IO_IN_DET_CFG_MASK;\n+\t\t\tval = SCL2367_IO_IN_DET_CFG(flow_type);\n+\t\t\treg = I3C_HUB_TP_SCL_IN_DETECT_FLG;\n+\t\t}\n+\t}\n+\n+\tret = regmap_write(hub->regmap, reg, BIT(tp));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = regmap_update_bits(hub->regmap, I3C_HUB_TP_IN_DETECT_MODE_CONF,\n+\t\t\t\t mask, val);\n+\treturn ret;\n+}\n+\n+static void i3c_hub_gpio_irq_bus_lock(struct irq_data *d)\n+{\n+\tstruct gpio_chip *gc = irq_data_get_irq_chip_data(d);\n+\tstruct i3c_hub *hub = gpiochip_get_data(gc);\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\n+\tmutex_lock(&gpio->irq_mutex);\n+}\n+\n+static void i3c_hub_gpio_irq_bus_unlock(struct irq_data *d)\n+{\n+\tstruct gpio_chip *gc = irq_data_get_irq_chip_data(d);\n+\tstruct i3c_hub *hub = gpiochip_get_data(gc);\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\n+\tmutex_unlock(&gpio->irq_mutex);\n+}\n+\n+static void i3c_hub_setup_gpio(struct i3c_hub *hub)\n+{\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\tstruct gpio_chip *gc = &gpio->chip;\n+\tstruct gpio_irq_chip *girq;\n+\n+\tgc->direction_input = i3c_hub_gpio_direction_input;\n+\tgc->direction_output = i3c_hub_gpio_direction_output;\n+\tgc->get = i3c_hub_gpio_get_value;\n+\tgc->set = i3c_hub_gpio_set_value;\n+\tgc->get_direction = i3c_hub_gpio_get_direction;\n+\tgc->can_sleep = true;\n+\n+\tgc->base = -1;\n+\tgc->ngpio = gpio->nums;\n+\tgc->label = dev_name(&hub->i3cdev->dev);\n+\tgc->parent = &hub->i3cdev->dev;\n+\tgc->owner = THIS_MODULE;\n+\n+\t/* irq_chip support */\n+\tmutex_init(&gpio->irq_mutex);\n+\n+\tgpio->irq_chip.name = dev_name(&hub->i3cdev->dev);\n+\tgpio->irq_chip.irq_mask = i3c_hub_gpio_irq_mask;\n+\tgpio->irq_chip.irq_unmask = i3c_hub_gpio_irq_unmask;\n+\tgpio->irq_chip.irq_set_type = i3c_hub_gpio_irq_set_type;\n+\tgpio->irq_chip.irq_bus_lock = i3c_hub_gpio_irq_bus_lock;\n+\tgpio->irq_chip.irq_bus_sync_unlock = i3c_hub_gpio_irq_bus_unlock;\n+\n+\tgirq = &gpio->chip.irq;\n+\n+\t/* This will let us handle the parent IRQ in the driver */\n+\tgirq->parent_handler = NULL;\n+\tgirq->num_parents = 0;\n+\tgirq->parents = NULL;\n+\tgirq->default_type = IRQ_TYPE_NONE;\n+\tgirq->handler = handle_simple_irq;\n+\tgirq->threaded = true;\n+\tgirq->first = 0;\n+\n+\tgirq->chip = &gpio->irq_chip;\n+}\n+\n+static void i3c_hub_io_ibi_handler(struct i3c_hub *hub,\n+\t\t\t\t const struct i3c_ibi_payload *payload)\n+{\n+\tstruct hub_gpio *gpio = &hub->gpio;\n+\tstruct gpio_chip *gc = &gpio->chip;\n+\tu8 level, hwirq, tmp;\n+\tu8 pending[GPIO_BANK_SZ];\n+\tu8 tp[GPIO_BANK_SZ];\n+\tint i, irq, ret, index;\n+\n+\tret = regmap_bulk_read(hub->regmap, I3C_HUB_TP_SCL_OUT_EN, tp,\n+\t\t\t GPIO_BANK_SZ);\n+\tif (ret) {\n+\t\tdev_err(&hub->i3cdev->dev, \"Failed to read OUT_EN: %d\\n\", ret);\n+\t\treturn;\n+\t}\n+\n+\tret = regmap_bulk_read(hub->regmap, I3C_HUB_TP_SCL_IN_DETECT_FLG,\n+\t\t\t pending, GPIO_BANK_SZ);\n+\tif (ret) {\n+\t\tdev_err(&hub->i3cdev->dev, \"Failed to read DETECT_FLG: %d\\n\",\n+\t\t\tret);\n+\t\treturn;\n+\t}\n+\n+\tfor (i = 0; i < GPIO_BANK_SZ; i++) {\n+\t\ttmp = ~tp[i] & pending[i];\n+\n+\t\twhile (tmp) {\n+\t\t\tlevel = __ffs(tmp);\n+\t\t\ttmp &= ~(1 << level);\n+\n+\t\t\t/* Check if this port is in GPIO mode */\n+\t\t\tindex = gpio->port_to_index[level];\n+\t\t\tif (index < 0) {\n+\t\t\t\t/* Non-GPIO mode port, skip without clearing.\n+\t\t\t\t * This can happen because IN_DETECT IBI enable is\n+\t\t\t\t * configured in groups (e.g., TP0145/TP2367), not\n+\t\t\t\t * per individual port. Simply skip - the flag is\n+\t\t\t\t * harmless and will be overwritten by next detection.\n+\t\t\t\t */\n+\t\t\t\tdev_dbg(&hub->i3cdev->dev,\n+\t\t\t\t\t\"IBI detect flag on non-GPIO port %d, skipping\\n\",\n+\t\t\t\t\tlevel);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\thwirq = index * 2 + i;\n+\t\t\tirq = irq_find_mapping(gc->irq.domain, hwirq);\n+\n+\t\t\t/* Clear the flag after processing */\n+\t\t\tregmap_write(hub->regmap,\n+\t\t\t\t I3C_HUB_TP_SCL_IN_DETECT_FLG + i,\n+\t\t\t\t BIT(level));\n+\n+\t\t\tif (unlikely(irq <= 0)) {\n+\t\t\t\tdev_warn_ratelimited(gc->parent,\n+\t\t\t\t\t\t \"unmapped interrupt %d\\n\",\n+\t\t\t\t\t\t hwirq);\n+\t\t\t} else {\n+\t\t\t\thandle_nested_irq(irq);\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n+\n+static void i3c_hub_ibi_handler(struct i3c_device *dev,\n+\t\t\t\tconst struct i3c_ibi_payload *payload)\n+{\n+\tstruct i3c_hub *priv = i3cdev_get_drvdata(dev);\n+\tint ret, val = 0;\n+\tu8 status = 0;\n+\n+\tif (!payload->len) {\n+\t\tdev_dbg(&dev->dev,\n+\t\t\t\"Zero-length IBI payload, reading status register\\n\");\n+\t\tret = regmap_read(priv->regmap, I3C_HUB_DEV_AND_IBI_STS, &val);\n+\t\tif (ret) {\n+\t\t\tdev_warn_ratelimited(&dev->dev,\n+\t\t\t\t\t \"Failed to read IBI status: %d\\n\",\n+\t\t\t\t\t ret);\n+\t\t\treturn;\n+\t\t}\n+\t\tstatus = (u8)val;\n+\t} else {\n+\t\tif (!payload->data) {\n+\t\t\tdev_warn_ratelimited(\n+\t\t\t\t&dev->dev,\n+\t\t\t\t\"IBI payload data is NULL with len=%d\\n\",\n+\t\t\t\tpayload->len);\n+\t\t\treturn;\n+\t\t}\n+\t\tstatus = ((const u8 *)payload->data)[0];\n+\t}\n+\n+\tif (status & TP_IO_FLAG_STATUS)\n+\t\ti3c_hub_io_ibi_handler(priv, payload);\n+\n+\tif (status & SMBUS_AGENT_EVENT_FLAG_STATUS) {\n+\t\tret = i3c_hub_smbus_ibi_handler(priv, payload);\n+\t\tif (ret) {\n+\t\t\tdev_warn_ratelimited(&dev->dev,\n+\t\t\t\t\t \"Failed to handle SMBus IBI: %d\\n\",\n+\t\t\t\t\t ret);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+}\n+\n+static inline void i3c_hub_regmap_lock(void *ctx)\n+{\n+\tmutex_lock(&i3c_hub_regmap_mutex);\n+}\n+\n+static inline void i3c_hub_regmap_unlock(void *ctx)\n+{\n+\tmutex_unlock(&i3c_hub_regmap_mutex);\n+}\n+\n+static int i3c_hub_probe(struct i3c_device *i3cdev)\n+{\n+\tconst struct regmap_config i3c_hub_regmap_config = {\n+\t\t.reg_bits = 8,\n+\t\t.val_bits = 8,\n+\t\t.lock = i3c_hub_regmap_lock,\n+\t\t.unlock = i3c_hub_regmap_unlock,\n+\t\t.lock_arg = NULL,\n+\t};\n+\tstruct device *dev = &i3cdev->dev;\n+\tstruct device_node *node = NULL;\n+\tstruct regmap *regmap;\n+\tstruct i3c_hub *priv;\n+\tchar hub_id[32];\n+\tint ret;\n+\tint i;\n+\tstruct i3c_ibi_setup ibireq = {};\n+\n+\tpriv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);\n+\tif (!priv)\n+\t\treturn -ENOMEM;\n+\n+\tpriv->i3cdev = i3cdev;\n+\tpriv->driving_master = i3c_dev_get_master(i3cdev->desc);\n+\tmutex_init(&priv->page_mutex);\n+\ti3cdev_set_drvdata(i3cdev, priv);\n+\tINIT_DELAYED_WORK(&priv->delayed_work, i3c_hub_delayed_work);\n+\ti3c_hub_of_default_configuration(dev);\n+\n+\tregmap = devm_regmap_init_i3c(i3cdev, &i3c_hub_regmap_config);\n+\tif (IS_ERR(regmap)) {\n+\t\tret = PTR_ERR(regmap);\n+\t\tdev_err(dev, \"Failed to register I3C HUB regmap\\n\");\n+\t\treturn ret;\n+\t}\n+\tpriv->regmap = regmap;\n+\n+\tpriv->dev_info = i3c_hub_lookup_dev_info(priv);\n+\tif (IS_ERR(priv->dev_info)) {\n+\t\tret = PTR_ERR(priv->dev_info);\n+\t\tdev_err(dev, \"Failed to lookup HUB dev info\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tsprintf(hub_id, \"i3c-hub-%d-%llx\",\n+\t\ti3c_dev_get_master(i3cdev->desc)->bus.id,\n+\t\ti3cdev->desc->info.pid);\n+\tret = i3c_hub_debugfs_init(priv, hub_id);\n+\tif (ret)\n+\t\tdev_dbg(dev, \"Failed to initialized DebugFS.\\n\");\n+\n+\tret = i3c_hub_read_id(dev);\n+\tif (ret)\n+\t\tgoto error;\n+\n+\tpriv->hub_dt_sel_id = -1;\n+\tpriv->hub_dt_cp1_id = -1;\n+\tif (priv->hub_pin_cp1_id >= 0 && priv->hub_pin_sel_id >= 0)\n+\t\t/* Find hub node in DT matching HW ID or just first without ID provided in DT */\n+\t\tnode = i3c_hub_get_dt_hub_node(dev->parent->of_node, priv);\n+\n+\tif (!node) {\n+\t\tdev_info(dev,\n+\t\t\t \"No DT entry - running with hardware defaults.\\n\");\n+\t} else {\n+\t\tof_node_get(node);\n+\t\ti3c_hub_of_get_conf_static(dev, node);\n+\t\ti3c_hub_of_get_conf_runtime(dev, node);\n+\t\tof_node_put(node);\n+\t}\n+\n+\t/* Unlock access to protected registers */\n+\tret = regmap_write(priv->regmap, I3C_HUB_PROTECTION_CODE,\n+\t\t\t REGISTERS_UNLOCK_CODE);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to unlock HUB's protected registers\\n\");\n+\t\tgoto error;\n+\t}\n+\n+\t/* Register logic for native smbus ports */\n+\tfor (i = 0; i < priv->dev_info->n_ports; i++) {\n+\t\tpriv->smbus_port_adapter[i].used = 0;\n+\t\tif (priv->settings.tp[i].mode == I3C_HUB_DT_TP_MODE_SMBUS) {\n+\t\t\tret = i3c_hub_smbus_tp_algo(priv, i);\n+\t\t\tif (ret)\n+\t\t\t\tdev_warn(\n+\t\t\t\t\tdev,\n+\t\t\t\t\t\"Failed to setup SMBus adapter, port: %d\\n\",\n+\t\t\t\t\ti);\n+\t\t}\n+\t}\n+\n+\tret = i3c_hub_configure_hw(dev);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to configure the HUB\\n\");\n+\t\tgoto error;\n+\t}\n+\n+\t/* Lock access to protected registers */\n+\tret = regmap_write(priv->regmap, I3C_HUB_PROTECTION_CODE,\n+\t\t\t REGISTERS_LOCK_CODE);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to lock HUB's protected registers\\n\");\n+\t\tgoto error;\n+\t}\n+\n+\t/* IBI */\n+\tibireq.handler = i3c_hub_ibi_handler;\n+\tibireq.max_payload_len = IBI_MAX_PAYLOAD_LEN;\n+\tibireq.num_slots = IBI_SLOT_NUMS;\n+\n+\tret = i3c_device_request_ibi(i3cdev, &ibireq);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to requeset ibi!\\n\");\n+\t\tgoto error;\n+\t}\n+\n+\tret = i3c_device_enable_ibi(i3cdev);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to enable ibi!\\n\");\n+\t\tgoto err_free_ibi;\n+\t}\n+\n+\t/* TBD: Apply special/security lock here using DEV_CMD register */\n+\n+\tif (priv->gpio.nums > 0) {\n+\t\ti3c_hub_setup_gpio(priv);\n+\n+\t\tret = devm_gpiochip_add_data(dev, &priv->gpio.chip, priv);\n+\t\tif (ret) {\n+\t\t\tdev_err(dev, \"gpiochip add data fail!\\n\");\n+\t\t\tgoto err_dis_ibi;\n+\t\t}\n+\t}\n+\n+\tschedule_delayed_work(&priv->delayed_work, msecs_to_jiffies(100));\n+\n+\treturn 0;\n+\n+err_dis_ibi:\n+\ti3c_device_disable_ibi(i3cdev);\n+err_free_ibi:\n+\ti3c_device_free_ibi(i3cdev);\n+error:\n+\tdebugfs_remove_recursive(priv->debug_dir);\n+\treturn ret;\n+}\n+\n+static void i3c_hub_remove(struct i3c_device *i3cdev)\n+{\n+\tstruct i3c_hub *priv = i3cdev_get_drvdata(i3cdev);\n+\tint i;\n+\n+\ti3c_device_disable_ibi(i3cdev);\n+\ti3c_device_free_ibi(i3cdev);\n+\n+\tfor (i = 0; i < priv->dev_info->n_ports; i++) {\n+\t\tif (priv->smbus_port_adapter[i].used) {\n+\t\t\tcancel_delayed_work_sync(\n+\t\t\t\t&priv->smbus_port_adapter[i]\n+\t\t\t\t\t .delayed_work_polling);\n+\t\t\ti2c_del_adapter(&priv->smbus_port_adapter[i].i2c);\n+\t\t}\n+\n+\t\tif (priv->logical_bus[i].registered)\n+\t\t\ti3c_master_unregister(&priv->logical_bus[i].controller);\n+\t}\n+\n+\tcancel_delayed_work_sync(&priv->delayed_work);\n+\tdebugfs_remove_recursive(priv->debug_dir);\n+}\n+\n+static struct i3c_driver i3c_hub = {\n+\t.driver.name = \"rts490xa-i3c-hub\",\n+\t.id_table = i3c_hub_ids,\n+\t.probe = i3c_hub_probe,\n+\t.remove = i3c_hub_remove,\n+};\n+\n+module_i3c_driver(i3c_hub);\n+\n+MODULE_DESCRIPTION(\"RTS490XA I3C HUB driver\");\n+MODULE_LICENSE(\"GPL\");\n", "prefixes": [ "2/2" ] }